var fs = require('fs'); var ipm2 = require('pm2-interface'); var rpc = require('pm2-axon-rpc'); var axon = require('pm2-axon'); var debug = require('debug')('interface:driver'); // Interface var chalk = require('chalk'); var pkg = require('../../package.json'); var cst = require('../../constants.js'); var Cipher = require('./Cipher.js'); var Filter = require('./Filter.js'); var ReverseInteractor = require('./ReverseInteractor.js'); var PushInteractor = require('./PushInteractor.js'); var WatchDog = require('./WatchDog.js'); var Url = require('url'); var os = require('os'); var HttpRequest = require('./HttpRequest.js'); var Daemon = { connectToPM2 : function() { return ipm2({bind_host: 'localhost'}); }, exit : function() { setTimeout(function() { if (!this._rpc || !this._rpc.sock) return process.exit(cst.ERROR_EXIT); this._rpc.sock.close(function() { process.exit(cst.SUCCESS_EXIT); }); }, 100); }, activateRPC : function() { console.log('Launching Interactor exposure'); var self = this; var rep = axon.socket('rep'); var daemon_server = new rpc.Server(rep); var sock = rep.bind(cst.INTERACTOR_RPC_PORT); daemon_server.expose({ kill : function(cb) { console.log('Killing interactor'); cb(null); Daemon.exit(); }, getInfos : function(cb) { return cb(null, { machine_name : self.opts.MACHINE_NAME, public_key : self.opts.PUBLIC_KEY, secret_key : self.opts.SECRET_KEY, remote_host : cst.REMOTE_HOST, remote_port : cst.REMOTE_PORT, reverse_interaction : self.opts.REVERSE_INTERACT }); } }); return daemon_server; }, formatMetada : function() { var cpu, memory; var self = this; try { cpu = os.cpus(); memory = Math.floor(os.totalmem() / 1024 / 1024); } catch(e) { cpu = 0; memory = 0; }; var ciphered_data = Cipher.cipherMessage(JSON.stringify({ MACHINE_NAME : this.opts.MACHINE_NAME, PUBLIC_KEY : this.opts.PUBLIC_KEY, PM2_VERSION : this.opts.PM2_VERSION, MEMORY : memory, HOSTNAME : os.hostname(), CPUS : cpu.length }), this.opts.SECRET_KEY); return ciphered_data; }, refreshWorker : function() { var self = this; function refreshMetadata() { var ciphered_data = Daemon.formatMetada(); HttpRequest.post({ url : self.opts.ROOT_URL, port : self.opts.ROOT_PORT, data : { public_id : self.opts.PUBLIC_KEY, data : ciphered_data } }, function(err, km_data) { if (err) return console.error(err); if (km_data.disabled == true) { console.error(chalk.cyan('[Keymetrics.io]') + ' Server DISABLED BY ADMINISTRATION contact support contact@keymetrics.io with reference to your public and secret keys)'); return Daemon.exit(); } /************************************** * Urls has changed = update workers * **************************************/ if ((Daemon.current_km_data.endpoints.push != km_data.endpoints.push) || (Daemon.current_km_data.endpoints.reverse != km_data.endpoints.reverse)) { console.log('[Interactor] Urls changed'); PushInteractor.changeUrl(km_data.endpoints.push); ReverseInteractor.changeUrl(km_data.endpoints.reverse); Daemon.current_km_data = km_data; } return false; }); }; // Refresh metadata every 10 minutes setInterval(function() { refreshMetadata(); }, 60000 * 10); }, validateData : function() { var opts = {}; opts.MACHINE_NAME = process.env.PM2_MACHINE_NAME; opts.PUBLIC_KEY = process.env.PM2_PUBLIC_KEY; opts.SECRET_KEY = process.env.PM2_SECRET_KEY; opts.REVERSE_INTERACT = JSON.parse(process.env.PM2_REVERSE_INTERACT); opts.PM2_VERSION = pkg.version; if (!opts.MACHINE_NAME) { console.error('You must provide a PM2_MACHINE_NAME environment variable'); process.exit(cst.ERROR_EXIT); } else if (!opts.PUBLIC_KEY) { console.error('You must provide a PM2_PUBLIC_KEY environment variable'); process.exit(cst.ERROR_EXIT); } else if (!opts.SECRET_KEY) { console.error('You must provide a PM2_SECRET_KEY environment variable'); process.exit(cst.ERROR_EXIT); } return opts; }, welcome : function(cb) { var self = this; var ciphered_data = Daemon.formatMetada(); if (!ciphered_data) { process.send({ msg : 'Error while ciphering data', error : true }); return process.exit(1); } HttpRequest.post({ url : self.opts.ROOT_URL, port : self.opts.ROOT_PORT, data : { public_id : this.opts.PUBLIC_KEY, data : ciphered_data } }, function(err, km_data) { self.current_km_data = km_data; if (err) { return cb(err); } // For Human feedback if (process.send) process.send({ error : false, km_data : km_data, online : true, pid : process.pid, machine_name : self.opts.MACHINE_NAME, public_key : self.opts.PUBLIC_KEY, secret_key : self.opts.SECRET_KEY, reverse_interaction : self.opts.REVERSE_INTERACT }); // Return get data return cb(null, km_data); }); }, start : function() { var self = this; self.opts = self.validateData(); self.opts.ipm2 = null; self.current_km_data = null; self._rpc = self.activateRPC(); // WatchDog.start({ // conf : self.opts // }); if (cst.DEBUG) { self.opts.ROOT_URL = '127.0.0.1'; if (process.env.NODE_ENV == 'test') self.opts.ROOT_PORT = 3400; else self.opts.ROOT_PORT = 3000; } else { self.opts.ROOT_URL = cst.KEYMETRICS_ROOT_URL; self.opts.ROOT_PORT = 443; } Daemon.welcome(function(err, km_data) { if (err) { process.send({ error : true, msg : err.stack || err }); return Daemon.exit(); } if (km_data.disabled == true) { console.error('Interactor disabled'); return Daemon.exit(); } if (km_data.pending == true) { console.error('Interactor pending'); return Daemon.exit(); } if (km_data.active == true) { self.opts.ipm2 = self.connectToPM2(); PushInteractor.start({ url : km_data.endpoints.push, conf : self.opts }); if (self.opts.REVERSE_INTERACT == true) { ReverseInteractor.start({ url : km_data.endpoints.reverse, conf : self.opts }); } Daemon.refreshWorker(); } }); } }; /** * MAIN */ if (require.main === module) { console.log(chalk.cyan.bold('[Keymetrics.io]') + ' Launching agent'); process.title = 'PM2: Keymetrics.io Agent'; Daemon.start(); }