pm2/lib/Interactor/InteractorDaemonizer.js
2014-08-24 22:03:43 +02:00

333 lines
8.9 KiB
JavaScript

'use strict';
var fs = require('fs');
var cst = require('../../constants.js');
var path = require('path');
var util = require('util');
var rpc = require('pm2-axon-rpc');
var Common = require('../Common');
var debug = require('debug')('pm2:interface:daemon');
var axon = require('axon');
var chalk = require('chalk');
var InteractorDaemonizer = module.exports = {};
InteractorDaemonizer.rpc = {};
/**
* Description
* @method ping
* @param {} cb
* @return
*/
InteractorDaemonizer.ping = function(cb) {
var req = axon.socket('req');
var client = new rpc.Client(req);
debug('[PING INTERACTOR] Trying to connect to Interactor daemon');
client.sock.once('reconnect attempt', function() {
client.sock.close();
debug('Interactor Daemon not launched');
cb(false);
});
client.sock.once('connect', function() {
client.sock.close();
debug('Interactor Daemon alive');
cb(true);
});
req.connect(cst.INTERACTOR_RPC_PORT);
};
InteractorDaemonizer.killDaemon = function(cb) {
debug('Killing interactor #1 ping');
InteractorDaemonizer.ping(function(online) {
debug('Interactor online', online);
if (!online) {
if (!cb) Common.printError('Interactor not launched');
return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.SUCCESS_EXIT);
}
InteractorDaemonizer.launchRPC(function() {
InteractorDaemonizer.rpc.kill(function(err) {
if (err) Common.printError(err);
setTimeout(function() {
InteractorDaemonizer.disconnectRPC(cb);
}, 100);
});
});
return false;
});
};
/**
* Description
* @method launchRPC
* @param {} cb
* @return
*/
InteractorDaemonizer.launchRPC = function(cb) {
var req = axon.socket('req');
this.client = new rpc.Client(req);
var self = this;
this.client_sock = req.connect(cst.INTERACTOR_RPC_PORT);
debug('Generating methods');
/**
* Description
* @method generateMethods
* @param {} cb
* @return
*/
var generateMethods = function(cb) {
self.client.methods(function(err, methods) {
Object.keys(methods).forEach(function(key) {
var method_signature = methods[key];
debug('+-- Creating %s method', method_signature.name);
(function(name) {
/**
* Description
* @method name
* @return
*/
self.rpc[name] = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift(name);
self.client.call.apply(self.client, args);
};
})(method_signature.name);
});
return cb();
});
};
this.client.sock.once('connect', function() {
generateMethods(function() {
debug('Methods generated');
cb();
});
});
};
/**
* Description
* @method launchOrAttach
* @param {} secret_key
* @param {} public_key
* @param {} machine_name
* @param {} cb
* @return
*/
function launchOrAttach(infos, cb) {
InteractorDaemonizer.ping(function(online) {
if (online) {
debug('Interactor online, restarting it...');
InteractorDaemonizer.launchRPC(function() {
InteractorDaemonizer.rpc.kill(function(err) {
InteractorDaemonizer.daemonize(infos, function() {
return cb(true);
});
});
});
}
else {
debug('Interactor offline, launching it...');
InteractorDaemonizer.daemonize(infos, function() {
return cb(true);
});
}
return false;
});
};
/**
* Description
* @method daemonize
* @param {} secret_key
* @param {} public_key
* @param {} machine_name
* @param {} cb
* @return
*/
InteractorDaemonizer.daemonize = function(infos, cb) {
var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js');
var out = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a'),
err = fs.openSync(cst.INTERACTOR_LOG_FILE_PATH, 'a');
var child = require('child_process').spawn('node', [InteractorJS], {
silent : false,
detached : true,
cwd : process.cwd(),
env : util._extend({
PM2_MACHINE_NAME : infos.machine_name,
PM2_SECRET_KEY : infos.secret_key,
PM2_PUBLIC_KEY : infos.public_key,
PM2_REVERSE_INTERACT : infos.reverse_interact
}, process.env),
stdio : ['ipc', out, err]
});
fs.writeFileSync(cst.INTERACTOR_PID_PATH, child.pid);
child.unref();
child.on('exit', function(msg) {
debug('Error when launching Interactor, please check the agent logs');
return cb(null, child);
});
debug('Waiting for message');
child.once('message', function(msg) {
debug('Interactor ready');
process.emit('interactor:daemon:ready');
//console.log(msg);
child.disconnect();
console.log(chalk.cyan('[Keymetrics.io]') + ' Launched - Log: %s | Conf: %s | PID: %s', cst.INTERACTOR_LOG_FILE_PATH,
cst.INTERACTION_CONF,
cst.INTERACTOR_PID_PATH);
return setTimeout(function() {cb(null, child)}, 100);
});
};
InteractorDaemonizer.update = function(cb) {
InteractorDaemonizer.ping(function(online) {
if (!online) {
Common.printError('Interactor not launched');
return cb ? cb({msg:'Interactor not launched'}) : Common.exitCli(cst.ERROR_EXIT);
}
InteractorDaemonizer.launchRPC(function() {
InteractorDaemonizer.rpc.kill(function(err) {
if (err) {
Common.printError(err);
return cb ? cb({msg : err}) : Common.exitCli(cst.ERROR_EXIT);
}
Common.printOut('Interactor successfully killed');
setTimeout(function() {
InteractorDaemonizer.launchAndInteract({}, function() {
return cb ? cb(null, {msg : 'killed'}) : Common.exitCli(cst.SUCCESS_EXIT);
});
}, 500);
});
});
return false;
});
};
/**
* Get interaction keys from
* - environment
* - file
* If keys are not set save them to configuration file\
*
* @param {object|string} secret_key|obj
*/
InteractorDaemonizer.getSetKeys = function(secret_key, public_key, machine_name, cb) {
var os = require('os');
var create_file = false;
var reverse_interact = false;
// If object
if (secret_key && typeof(secret_key) == 'object') {
var cpy = JSON.parse(JSON.stringify(secret_key));
cb = public_key;
secret_key = cpy.secret_key;
public_key = cpy.public_key;
machine_name = cpy.machine_name;
}
try {
var interaction_conf = JSON.parse(fs.readFileSync(cst.INTERACTION_CONF));
secret_key = secret_key ? secret_key : interaction_conf.secret_key;
public_key = public_key ? public_key : interaction_conf.public_key;
machine_name = machine_name ? machine_name : interaction_conf.machine_name;
reverse_interact = interaction_conf.reverse_interact || false;
} catch (e) {
debug('Interaction file does not exists');
create_file = true;
}
if (!secret_key) {
if (!process.env.PM2_SECRET_KEY)
return cb ? cb({msg:'secret key is not defined'}) : Common.exitCli(cst.ERROR_EXIT);
secret_key = process.env.PM2_SECRET_KEY;
}
if (!public_key) {
if (!process.env.PM2_PUBLIC_KEY)
return cb ? cb({msg:'public key is not defined'}) : Common.exitCli(cst.ERROR_EXIT);
public_key = process.env.PM2_PUBLIC_KEY;
}
if (!machine_name) {
machine_name = os.hostname();
}
/**
* Write new data to configuration file
*/
try {
var new_interaction_conf = {
secret_key : secret_key,
public_key : public_key,
machine_name : machine_name,
reverse_interact : reverse_interact
};
fs.writeFileSync(cst.INTERACTION_CONF, JSON.stringify(new_interaction_conf));
} catch(e) {
console.error('Error when writting configuration file %s', cst.INTERACTION_CONF);
}
return cb(null, {
secret_key : secret_key,
public_key : public_key,
machine_name : machine_name,
reverse_interact : reverse_interact
});
};
InteractorDaemonizer.disconnectRPC = function(cb) {
process.nextTick(function() {
if (!InteractorDaemonizer.client_sock ||
!InteractorDaemonizer.client_sock.close)
return cb(null, {
success : false,
msg : 'RPC connection to Interactor Daemon is not launched'
});
debug('Closing RPC INTERACTOR');
InteractorDaemonizer.client_sock.close();
return cb ? cb(null, {success:true}) : false;
});
};
InteractorDaemonizer.launchAndInteract = function(opts, cb) {
if (process.env.PM2_AGENT_ONLINE) {
return process.nextTick(cb);
}
InteractorDaemonizer.getSetKeys({
secret_key : opts.secret_key || null,
public_key : opts.public_key || null,
machine_name : opts.machine_name || null
}, function(err, data) {
if (err) {
debug('Cant get set keys');
return cb ? cb({msg:'Error when getting / setting keys'}) : Common.exitCli(cst.ERROR_EXIT);
}
launchOrAttach(data, function(status) {
return cb ? cb(null, {success:true}) : Common.exitCli(cst.SUCCESS_EXIT);
});
return false;
});
};