(agent) allow to specify --info-node + keep it persistent over updates

This commit is contained in:
Unitech 2017-01-29 16:47:47 +01:00
parent 907d200ea9
commit 58f9a8dc9d
5 changed files with 175 additions and 194 deletions

View File

@ -519,6 +519,7 @@ commander.command('unset <key>')
commander.command('link [secret_key|command] [public_key] [machine_name]')
.alias('interact')
.option('--info-node [url]', 'set url info node')
.description('linking action to keymetrics.io - command can be stop|info|delete|restart')
.action(pm2._pre_interact.bind(pm2));

View File

@ -392,7 +392,7 @@ API.prototype.update = function(cb) {
that.resurrect(function() {
Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));
Modularizer.launchAll(that, function() {
KMDaemon.launchAndInteract(that._conf, {}, function(err, data, interactor_proc) {
KMDaemon.launchAndInteract(that._conf, null, function(err, data, interactor_proc) {
// Interactor error can be skipped here
return cb ? cb(null, {success:true}) : that.speedList();
});
@ -873,7 +873,7 @@ API.prototype._startJson = function(file, opts, action, pipe, cb) {
appConf = config.apps;
else
appConf = config;
if (!Array.isArray(appConf))
appConf = [appConf]; //convert to array
@ -1633,7 +1633,7 @@ API.prototype.deepUpdate = function(cb) {
/**
* API method to launch a process that will serve directory over http
*
*
* @param {Object} opts options
* @param {String} opts.path path to be served
* @param {Number} opts.port port on which http will bind
@ -1656,7 +1656,7 @@ API.prototype.serve = function (opts, cb) {
opts.env['SERVE_PORT'] = opts.env['SERVE_PORT'] || servePort;
opts.env['SERVE_PATH'] = opts.env['SERVE_PATH'] || servePath;
opts.name = opts.name || servePath;
this.start(path.resolve(__dirname, 'API', 'Serve.js'), opts, function (err, res) {
if (err) {

View File

@ -11,6 +11,8 @@ var KMDaemon = require('../Interactor/InteractorDaemonizer');
module.exports = function(CLI) {
/**
* Launch interactor
* For programmatic interaction
* http://pm2.keymetrics.io/docs/usage/use-pm2-with-cloud-providers/
* @method interact
* @param {string} secret_key
* @param {string} public_key
@ -42,10 +44,10 @@ module.exports = function(CLI) {
//
// Interact
//
CLI.prototype._pre_interact = function(secret_key, public_key, machine, opts) {
CLI.prototype._pre_interact = function(cmd, public_key, machine, info_node) {
var that = this;
if (secret_key == 'stop' || secret_key == 'kill') {
if (cmd == 'stop' || cmd == 'kill') {
console.log(chalk.cyan('[Keymetrics.io]') + ' Stopping agent...');
that.killInteract(function() {
console.log(chalk.cyan('[Keymetrics.io]') + ' Stopped');
@ -53,7 +55,8 @@ module.exports = function(CLI) {
});
return false;
}
if (secret_key == 'info') {
if (cmd == 'info') {
console.log(chalk.cyan('[Keymetrics.io]') + ' Getting agent information...');
that.interactInfos(function(err, infos) {
if (err) {
@ -65,7 +68,8 @@ module.exports = function(CLI) {
});
return false;
}
if (secret_key == 'delete') {
if (cmd == 'delete') {
that.killInteract(function() {
try {
fs.unlinkSync(cst.INTERACTION_CONF);
@ -78,13 +82,45 @@ module.exports = function(CLI) {
});
return false;
}
if (secret_key == 'start' || secret_key == 'restart')
return that.interact(null, null, null);
if (secret_key && !public_key) {
console.error(chalk.cyan('[Keymetrics.io]') + ' Command [%s] unknown or missing public key', secret_key);
if (cmd == 'start' || cmd == 'restart') {
KMDaemon.launchAndInteract(that._conf, {
public_key : null,
secret_key : null,
machine_name : null,
info_node : null
}, function(err, dt) {
if (err) {
Common.printError(err);
return that.exitCli(cst.ERROR_EXIT);
}
return that.exitCli(cst.SUCCESS_EXIT);
});
}
if (cmd && !public_key) {
console.error(chalk.cyan('[Keymetrics.io]') + ' Command [%s] unknown or missing public key', cmd);
return process.exit(cst.ERROR_EXIT);
}
return that.interact(secret_key, public_key, machine);
var infos;
if (!cmd) {
infos = null;
}
else
infos = {
public_key : public_key,
secret_key : cmd,
machine_name : machine,
info_node : info_node.infoNode || null
}
KMDaemon.launchAndInteract(that._conf, infos, function(err, dt) {
if (err)
return that.exitCli(cst.ERROR_EXIT);
return that.exitCli(cst.SUCCESS_EXIT);
});
};
/**

View File

@ -13,10 +13,11 @@ var util = require('util');
var rpc = require('pm2-axon-rpc');
var axon = require('pm2-axon');
var chalk = require('chalk');
var os = require('os');
var cst = require('../../constants.js');
var Common = require('../Common');
var json5 = require('../tools/json5.js');
var UX = require('../API/CliUx.js');
var InteractorDaemonizer = module.exports = {};
@ -187,9 +188,6 @@ function launchOrAttach(conf, infos, cb) {
* @param {} cb
* @return
*/
var UX = require('../API/CliUx.js');
var daemonize = function(conf, infos, cb) {
var InteractorJS = path.resolve(path.dirname(module.filename), 'Daemon.js');
@ -216,7 +214,8 @@ var daemonize = function(conf, infos, cb) {
PM2_MACHINE_NAME : infos.machine_name,
PM2_SECRET_KEY : infos.secret_key,
PM2_PUBLIC_KEY : infos.public_key,
PM2_REVERSE_INTERACT : infos.reverse_interact
PM2_REVERSE_INTERACT : infos.reverse_interact,
KEYMETRICS_NODE : infos.info_node
}, process.env),
stdio : ['ipc', out, err]
});
@ -308,79 +307,76 @@ InteractorDaemonizer.update = function(conf, cb) {
};
/**
* Get interaction keys from
* - environment
* - file
* If keys are not set save them to configuration file\
*
* @param {object|string} secret_key|obj
* Get/Update/Merge agent configuration
* @param {object} _infos
*/
InteractorDaemonizer.getSetKeys = function(conf, secret_key, public_key, machine_name, cb) {
var os = require('os');
/**
* Default values
*/
var create_file = false;
InteractorDaemonizer.getOrSetConf = function(conf, infos, cb) {
var reverse_interact = true;
var version_management_active = true;
var version_management_password = null;
// If object
if (secret_key && typeof(secret_key) == 'object') {
var cpy = json5.parse(json5.stringify(secret_key));
cb = public_key;
secret_key = cpy.secret_key;
public_key = cpy.public_key;
machine_name = cpy.machine_name;
}
var version_management_active = true;
var version_management_password = null;
var secret_key;
var public_key;
var machine_name;
var info_node;
var new_connection = false;
// 1# Load configuration file
try {
var interaction_conf = json5.parse(fs.readFileSync(conf.INTERACTION_CONF));
public_key = interaction_conf.public_key;
machine_name = interaction_conf.machine_name;
secret_key = interaction_conf.secret_key;
info_node = interaction_conf.info_node;
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 || true;
secret_key = secret_key ? secret_key : interaction_conf.secret_key;
if (interaction_conf.version_management) {
version_management_password = interaction_conf.version_management.password || version_management_password;
version_management_active = interaction_conf.version_management.active || version_management_active;
}
} catch (e) {
debug('Interaction file does not exists');
create_file = true;
}
// 2# Override with passed informations
if (infos) {
secret_key = infos.secret_key;
public_key = infos.public_key;
machine_name = infos.machine_name;
info_node = infos.info_node;
new_connection = true;
}
// 3# Override with environment variables (highest-priority conf)
if (process.env.PM2_SECRET_KEY || process.env.KEYMETRICS_SECRET)
secret_key = process.env.PM2_SECRET_KEY || process.env.KEYMETRICS_SECRET;
if (process.env.PM2_PUBLIC_KEY || process.env.KEYMETRICS_PUBLIC)
public_key = process.env.PM2_PUBLIC_KEY || process.env.KEYMETRICS_PUBLIC;
if (new_connection && infos.info_node == null)
info_node = process.env.KEYMETRICS_NODE || cst.KEYMETRICS_ROOT_URL;
if (!secret_key)
return cb(new Error('secret key is not defined'));
if (!public_key)
return cb(new Error('public key is not defined'));
if (!machine_name) {
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,
info_node : info_node,
version_management : {
active : version_management_active,
password : version_management_password
@ -389,11 +385,10 @@ InteractorDaemonizer.getSetKeys = function(conf, secret_key, public_key, machine
fs.writeFileSync(conf.INTERACTION_CONF, json5.stringify(new_interaction_conf, null, 4));
} catch(e) {
console.error('Error when writting configuration file %s', conf.INTERACTION_CONF);
return cb(e);
}
/**
* Don't block the event loop
*/
// Don't block the event loop
process.nextTick(function() {
cb(null, new_interaction_conf);
});
@ -448,7 +443,7 @@ InteractorDaemonizer.launchAndInteract = function(conf, opts, cb) {
process.env.PM2_INTERACTOR_PROCESSING = true;
InteractorDaemonizer.getSetKeys(conf, opts, function(err, data) {
InteractorDaemonizer.getOrSetConf(conf, opts, function(err, data) {
if (err || !data) {
return cb(err);
}

View File

@ -7,34 +7,114 @@ var interactorDaemonizer = require('../../lib/Interactor/InteractorDaemonizer');
var json5 = require('../../lib/tools/json5.js');
describe('Daemonizer interactor', function() {
before(function(done) {
delete process.env.PM2_SECRET_KEY;
delete process.env.PM2_PUBLIC_KEY;
delete process.env.KEYMETRICS_NODE;
try {
fs.unlinkSync(default_conf.INTERACTION_CONF);
} catch(e) {
}
} catch(e) {}
done();
});
describe('General tests', function() {
it('should try get set keys but get error because nothing exposed', function(done) {
interactorDaemonizer.getSetKeys(default_conf, null, null, null, function(err, data) {
interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) {
err.should.not.be.null();
done();
});
});
});
it('should work with env variables and create file', function(done) {
describe('Default behavior', function() {
after(function() {
fs.unlinkSync(default_conf.INTERACTION_CONF);
});
it('should set right node by default', function(done) {
interactorDaemonizer.getOrSetConf(default_conf, {
secret_key : 'xxx',
public_key : 'yyy',
machine_name : null,
info_node : null
}, function(err, data) {
should(err).be.null();
data.info_node.should.eql(default_conf.KEYMETRICS_ROOT_URL);
return done();
});
});
it('should retrieve data from file without env variable', function(done) {
interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('xxx');
data.public_key.should.eql('yyy');
data.info_node.should.eql(default_conf.KEYMETRICS_ROOT_URL);
return done();
});
});
it('should set new keys and write in configuration file', function(done) {
interactorDaemonizer.getOrSetConf(default_conf, {
secret_key : 'XXXS2',
public_key : 'XXXP2',
info_node : 'test2.url'
}, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
data.info_node.should.eql('test2.url');
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS2');
interaction_conf.public_key.should.eql('XXXP2');
interaction_conf.info_node.should.eql('test2.url');
should.exist(interaction_conf.version_management.active);
should(interaction_conf.version_management.password).be.null();
interaction_conf.machine_name.should.eql(os.hostname());
return done();
});
});
it('should retrieve data from file without env variable', function(done) {
interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
data.info_node.should.eql('test2.url');
return done();
});
});
});
describe('Environment variable override', function() {
before(function() {
process.env.PM2_SECRET_KEY = 'XXXS';
process.env.PM2_PUBLIC_KEY = 'XXXP';
process.env.KEYMETRICS_NODE = 'test.url';
});
interactorDaemonizer.getSetKeys(default_conf, null, null, null, function(err, data) {
after(function() {
delete process.env.PM2_SECRET_KEY;
delete process.env.PM2_PUBLIC_KEY;
delete process.env.KEYMETRICS_NODE;
});
it('should work with env variables and create file', function(done) {
interactorDaemonizer.getOrSetConf(default_conf, {
secret_key : null,
public_key : null,
machine_name : null,
info_node : null
}, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS');
data.public_key.should.eql('XXXP');
data.info_node.should.eql('test.url');
should.exist(data.version_management.active);
should(data.version_management.password).be.null();
@ -43,149 +123,18 @@ describe('Daemonizer interactor', function() {
} catch(e) {
return done(e);
}
delete process.env.PM2_SECRET_KEY;
delete process.env.PM2_PUBLIC_KEY;
return done();
});
});
it('should retrieve data from file without env variable', function(done) {
interactorDaemonizer.getSetKeys(default_conf, null, null, null, function(err, data) {
interactorDaemonizer.getOrSetConf(default_conf, null, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS');
data.public_key.should.eql('XXXP');
return done();
});
});
it('should set new keys and write in configuration file', function(done) {
interactorDaemonizer.getSetKeys(default_conf, 'XXXS2', 'XXXP2', null, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS2');
interaction_conf.public_key.should.eql('XXXP2');
should.exist(interaction_conf.version_management.active);
should(interaction_conf.version_management.password).be.null();
interaction_conf.machine_name.should.eql(os.hostname());
return done();
});
});
it('should work with object passed instead of direct params', function(done) {
interactorDaemonizer.getSetKeys(default_conf, {
secret_key : 'XXXS3',
public_key : 'XXXP3'
}, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS3');
data.public_key.should.eql('XXXP3');
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS3');
interaction_conf.public_key.should.eql('XXXP3');
interaction_conf.machine_name.should.eql(os.hostname());
data.info_node.should.eql('test.url');
return done();
});
});
});
describe.skip('Recycle option', function() {
it('should handle recycle option', function(done) {
interactorDaemonizer.getSetKeys(default_conf, 'XXXS2', 'XXXP2', null, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
data.recycle.should.be.true;
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS2');
interaction_conf.public_key.should.eql('XXXP2');
interaction_conf.recycle.should.be.true;
should.exist(interaction_conf.version_management.active);
should(interaction_conf.version_management.password).be.null();
interaction_conf.machine_name.should.eql(os.hostname());
return done();
});
});
it('should handle recycle option (obj like)', function(done) {
interactorDaemonizer.getSetKeys({
secret_key : 'XXXS2',
public_key : 'XXXP2',
machine_name : null,
recycle : true
}, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
data.recycle.should.be.true;
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS2');
interaction_conf.public_key.should.eql('XXXP2');
interaction_conf.recycle.should.be.true;
should.exist(interaction_conf.version_management.active);
should(interaction_conf.version_management.password).be.null();
interaction_conf.machine_name.should.eql(os.hostname());
return done();
});
});
it('should handle recycle option opts2', function(done) {
interactorDaemonizer.getSetKeys(null, null, null, null, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
data.recycle.should.be.true;
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS2');
interaction_conf.public_key.should.eql('XXXP2');
interaction_conf.recycle.should.be.true;
should.exist(interaction_conf.version_management.active);
should(interaction_conf.version_management.password).be.null();
interaction_conf.machine_name.should.eql(os.hostname());
return done();
});
});
it('should stop recycle option if passing secret and pub', function(done) {
interactorDaemonizer.getSetKeys({
secret_key : 'XXXS2',
public_key : 'XXXP2',
machine_name : null
}, function(err, data) {
should(err).be.null();
data.secret_key.should.eql('XXXS2');
data.public_key.should.eql('XXXP2');
data.recycle.should.be.false;
var interaction_conf = json5.parse(fs.readFileSync(default_conf.INTERACTION_CONF));
interaction_conf.secret_key.should.eql('XXXS2');
interaction_conf.public_key.should.eql('XXXP2');
interaction_conf.recycle.should.be.false;
should.exist(interaction_conf.version_management.active);
should(interaction_conf.version_management.password).be.null();
interaction_conf.machine_name.should.eql(os.hostname());
return done();
});
});
});
});