Skip to content

Instantly share code, notes, and snippets.

@tjfontaine
Created October 10, 2013 08:26
Show Gist options
  • Save tjfontaine/6914917 to your computer and use it in GitHub Desktop.
Save tjfontaine/6914917 to your computer and use it in GitHub Desktop.
var execFile = require('child_process').execFile;
var EE = require('events').EventEmitter;
var util = require('util');
var vm = require('vm');
module.exports = Charm;
function Charm(options) {
if (!(this instanceof Charm)) return new Charm(options);
// delay a tick so things can attach, maybe have a separate init that users pass in instead of this being magic on first require
var self = this;
process.nextTick(function() {
//argv[0] is node, argv[1] is script name, the rest will be args for the hook
var hook = process.argv[1];
//if we have defined a prototype method for this call it instead. otherwise re-emit
if (self[hook])
self[hook].apply(self, process.argv.slice(2));
else
self.emit.apply(self, process.argv.slice(1));
});
// assuming charm.js is in lib/charm.js the config file defaults to config/config.js
this.CONFIG_FILE = options.config || path.join(__dirname, '..', 'config', 'config.js');
}
util.inherits(Charm, EE);
Charm.prototype.relation_get = function(key, cb) {
this.exec('relation-get', [key], cb);
};
// should be a dict I guess?
Charm.prototype.relation_set = function(key, value, cb) {
this.exec('relation-set', [key, value], cb);
};
Charm.prototype.log = function(msg, cb) {
this.exec('juju-log', [msg], cb);
};
/* evil this exits like set -e does */
Charm.prototype.exec = function(cmd, args, cb) {
execFile(cmd, args, function(err, stderr, stdout) {
if (err) {
self.log(util.format('failed to run: %s %j %j', cmd, args, err));
process.exit(1);
return;
}
cb(stdout ? stdout.trim() : '');
});
};
Charm.prototype.configUpdate = function(section, value, cb) {
var self = this;
fs.readFile(this.CONFIG_FILE, 'utf8', function(err, value) {
// todo err handling -- like file creation?
var config = vm.runInNewContext('(function(){ return ' + value + ' })()');
var s = config[section] || {};
util._extend(s, value);
config[section] = s;
});
};
Charm.prototype['database-relation-changed'] = function() {
var self = this;
// use control flow mechanism to avoid callback hell
// preferably vasync
self.relation_get('user', function(user) {
self.relation_get('db', function(db) {
self.relation_get('pass', function(pass) {
self.relation_get('private-address', function(addr) {
self.emit('database-relation-changed', user, db, pass, addr);
});
});
});
});
};
{
'database': {
},
}
#!/usr/bin/env node
var charm = require('charm')();
charm.on('database-relation-changed', function(user, db, pass, addr) {
charm.configUpdate('database', {
user: user,
db: db,
pass: pass,
addr: addr,
}, function() {
// config successfully updated
});
});
#!/usr/bin/env node
var charm = require('charm')();
charm.on('start', function() {
charm.exec('service', ['node-app', 'start'], function(ret) {
});
});
@kapilt
Copy link

kapilt commented Oct 10, 2013

You can fetch all the params for a relation in a single relation-get call. you should also test conditionally those values have been set, and exit otherwise, the other side may not have had a chance to set values on the relation yet when the hook executes. juju guarantees the execution of relation-changed again when the remote settings are updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment