Skip to content

Commit

Permalink
Initial commit with code from https://gist.github.com/eveiga/5039007
Browse files Browse the repository at this point in the history
…wrapped in an npm module
  • Loading branch information
matschaffer committed Feb 26, 2013
0 parents commit 8f9eb18
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
/node_modules
/*.tgz
5 changes: 5 additions & 0 deletions README.md
@@ -0,0 +1,5 @@
# Redis-Twemproxy Agent

This originally started as a [gist](https://gist.github.com/eveiga/5039007) posted on [a twemproxy ticket about doing HA redis](https://github.com/twitter/twemproxy/issues/67).

Edgar was nice enough to share what he had so far and now I'm trying to take it a bit further in hopes of making a generalized package.
4 changes: 4 additions & 0 deletions bin/redis_twemproxy_agent
@@ -0,0 +1,4 @@
#!/usr/bin/env node

var path = require('path');
require(path.join(__dirname,'../lib/cli.js'));
122 changes: 122 additions & 0 deletions lib/agent.js
@@ -0,0 +1,122 @@
var fs = require('fs'),
exec = require('child_process').exec,
path = require('path'),
os = require('os');

var Sentinel = require("node-sentinel"),
_ = require("underscore"),
async = require("async"),
nodemailer = require("nodemailer");

function Agent(config){
if(!_.isObject(config)){
return console.error("Bad config");
}

this.nutcracker_config_file = config.nutcracker_config_file;
this.redis_sentinel_ip = config.redis_sentinel_ip;
this.redis_sentinel_port = config.redis_sentinel_port;
this.log = "";
}

Agent.prototype.restart_nutcracker = function(callback){
var self = this;
var child = exec(
"/etc/init.d/nutcracker restart",
function(error, stdout, stderr) {
self.log += "<h2>Nutcracker restarted with outuput:</h2>";
self.log += "<div><pre>"+stdout+"</pre></div>";

if (error !== null) {
self.log += "<h2>Nutcracker failed restarting with error:</h2>";
self.log += "<div><pre>"+error+"</pre></div>";
}
return callback();
}
);
};

Agent.prototype.update_nutcracker_config = function(data, callback){
var old_content = fs.readFileSync(this.nutcracker_config_file, 'utf8');

var new_content = old_content.replace(
data.details["old-ip"]+":"+data.details["old-port"],
data.details["new-ip"]+":"+data.details["new-port"]
);

fs.writeFileSync(this.nutcracker_config_file, new_content, 'utf8');

this.log += "<h2>Nutcracker config updated with new content:</h2>";
this.log += "<div><pre>"+new_content+"</pre></div>";

return callback();
};

Agent.prototype.send_mail = function() {
var self = this;
var transport = nodemailer.createTransport("Sendmail");

var mailOptions = {
from: "from@mail.com",
to: "to@mail.com",
subject: "Warning: Nutcracker restarted forced on "+os.hostname(),
generateTextFromHTML: true,
html: "<h1>Nutcracker was restarted due to master switch in redis configuration.</h1>" + this.log
};

transport.sendMail(mailOptions, function(error, responseStatus){
self.log = "";
if(error) {
return console.error(error);
}

return console.info(
responseStatus.message+"\n"+responseStatus.messageId
);
});
};

Agent.prototype.switch_master_handler = function(){
var self = this;
return function(data) {
async.series([
function(callback) {
self.update_nutcracker_config(data, callback);
},
function(callback) {
self.restart_nutcracker(callback);
}
],
function(){
return self.send_mail();
});
};
};

Agent.prototype.start_sentinel = function(){
this.sentinel = new Sentinel(
this.redis_sentinel_ip, this.redis_sentinel_port
);

this.sentinel.on("switch-master", this.switch_master_handler());
};

Agent.prototype.check_nutcracker_config = function(cb){
fs.appendFile(this.nutcracker_config_file, "", cb);
};

Agent.prototype.bootstrap = function(){
var self = this;

this.check_nutcracker_config(
function(error){
if(error) {
return console.error("Nutcracker config file: "+error);
}

return self.start_sentinel();
}
);
};

module.exports = Agent;
3 changes: 3 additions & 0 deletions lib/cli.js
@@ -0,0 +1,3 @@
var util = require('util');

util.puts("The CLI will go here");
35 changes: 35 additions & 0 deletions package.json
@@ -0,0 +1,35 @@
{
"name": "redis_twemproxy_agent",
"version": "0.0.1",
"description": "Redis agent for nutcracker update",
"preferGlobal": true,
"bin": "bin/redis_twemproxy_agent",
"main": "lib/agent.js",
"scripts": {
"test": "jshint lib spec && jasmine-node spec"
},
"repository": {
"type": "git",
"url": "https://github.com/matschaffer/redis_twemproxy_agent.git"
},
"keywords": [
"redis",
"twemproxy",
"ha"
],
"author": "Edgar Veiga <edgarmveiga@gmail.com>",
"contributors": [
"Mat Schaffer <mat@schaffer.me>"
],
"license": "BSD",
"dependencies": {
"node-sentinel": "0.0.4",
"underscore": "~1.4.4",
"async": "~0.2.5",
"nodemailer": "~0.3.42"
},
"devDependencies": {
"jshint": "~1.0.0",
"jasmine-node": "~1.3.0"
}
}
7 changes: 7 additions & 0 deletions spec/agent_spec.js
@@ -0,0 +1,7 @@
var Agent = require("../lib/agent.js");

describe("Redis Twemproxy Agent", function() {
it("subscribes to master switch notifications");
it("swaps old master information with new master information");
it("restarts twemproxy");
});

0 comments on commit 8f9eb18

Please sign in to comment.