Skip to content

Commit

Permalink
add test for R11 and R10
Browse files Browse the repository at this point in the history
  • Loading branch information
Filirom1 committed Dec 12, 2012
1 parent 02f3627 commit 7166f72
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 58 deletions.
44 changes: 44 additions & 0 deletions rukorun/checkBind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var fs = require('fs');
var _ = require('underscore');

module.exports = function(wantedPort, cb){

// $ cat /proc/net/tcp
// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
// 0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 78603 1 0000000000000000 100 0 0 10 -1
fs.readFile('/proc/net/tcp', function(err, data){
if(err){
console.error(err);
// swallow the error
return cb();
}
var lines = data.toString().split('\n').splice(1);
lines.forEach(function(line){
var arr = _.compact(line.split(' '));
if(arr[2] != "00000000:0000") return; // only looking for listening TCP sockets
if(arr[7] != "1666") return; // only looking sockets created by ruko user

var localAddress = arr[1].split(':');
var host = humanizeHostname(localAddress[0]);
var port = parseInt(localAddress[1], 16);
if(host !== '0.0.0.0') {
return cb(new Error('Error R11 (Bad bind) -> Process bound to host ' + host + ', should be 0.0.0.0'));
}
if(port !== wantedPort){
return cb(new Error('Error R11 (Bad bind) -> Process bound to port ' + port + ', should be ' + wantedPort + ' (see environment variable PORT)'));
}
cb(null, {
host: host,
port: port
});
});

// if nothing is found return
cb();
});
};

// trasnform 00000000 into 0.0.0.0
function humanizeHostname(str){
return str.match(/.{2}/g).map(function(hex){ return parseInt(hex, 16) }).join('.');
}
62 changes: 55 additions & 7 deletions rukorun/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ var cp = require('child_process');
var pty = require('pty.js');
var async = require('async');
var _ = require('underscore');
var checkBind = require('./checkBind');

var socketPath = process.argv[2];
var cwd = process.argv[3];
var killTimeout = process.argv[4];
var checkBindInterval = process.argv[5];
var bootTimeout = process.argv[6];

var ioSocket = net.createConnection(Path.join(socketPath, 'io.sock'));
var commandSocket = net.createConnection(Path.join(socketPath, 'command.sock'));
Expand Down Expand Up @@ -55,6 +58,47 @@ function processCommands() {
inst = spawn(payload, ioSocket, commandSocket);
}

if(payload.env_vars.PORT){
var isBound = false;

var bootTimeoutId = setTimeout(function(){
if(isBound) return;

sendToDynohost({
message: 'Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch'
});
return kill('SIGKILL');
}, bootTimeout);

async.until(function(){
return isBound;
}, function(cb){
checkBind(payload.env_vars.PORT, function(err, host){
if(err) return cb(err);

if(host){
return isBound = true;
}

// check every 1s
setTimeout(cb, checkBindInterval)
});
}, function(err){
if(err){
sendToDynohost({
message: err.message
});
return kill('SIGKILL');
}

sendToDynohost({
type: 'bound'
});

clearTimeout(bootTimeoutId);
});
}

inst.on('exit', function(code) {
// pty.js does not forward the exit code
// https://github.com/chjj/pty.js/issues/28
Expand All @@ -73,24 +117,28 @@ function processCommands() {
throw new Error('WTF try to exit a non existing inst');
}

sendToDynohost({
message: 'Stopping all processes with SIGTERM'
});
inst.kill('SIGTERM');
kill('SIGTERM');

setTimeout(function(){
sendToDynohost({
message: ['Error R12 (Exit timeout) -> At least one process failed to exit within 10 seconds of SIGTERM',
'Stopping all processes with SIGKILL'].join('\n')
message: 'Error R12 (Exit timeout) -> At least one process failed to exit within 10 seconds of SIGTERM',
});
inst.kill('SIGKILL');

kill('SIGKILL');
}, killTimeout || 10000);
}
});

function sendToDynohost(object){
commandSocket.write(JSON.stringify(object) + '\n');
}

function kill(signal){
sendToDynohost({
message: 'Stopping all processes with ' + signal
});
inst.kill(signal);
}
}

// Used when `openruko run bash`
Expand Down
7 changes: 7 additions & 0 deletions test/fixture/badHost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var http = require('http');
var port = process.env.PORT;
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(port, '127.0.0.1');
console.log('Server running at http://127.0.0.1:' + port + '/');
6 changes: 6 additions & 0 deletions test/fixture/badPort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(6666);
console.log('Server running at http://127.0.0.1:6666/');
4 changes: 4 additions & 0 deletions test/fixture/setTimeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
console.log('setTimeout started');
setTimeout(function(){
console.log('After a long long time');
}, 10000000);
185 changes: 134 additions & 51 deletions test/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ describe('rukorun', function(){
if(err) return done(err);
command = arr[0];
io = arr[1];

io.pipe(process.stdout, {end: false});
command.pipe(process.stdout, {end: false});
done();
});

Expand All @@ -31,16 +34,18 @@ describe('rukorun', function(){
Path.join(__dirname, '../rukorun/run.js'),
pathTest,
__dirname,
100,
10,
200
]);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
child.stdout.pipe(process.stdout, {end: false});
child.stderr.pipe(process.stderr, {end: false});
}, 20);
});

afterEach(function(done){
child.kill('SIGTERM');
setTimeout(done, 200);
setTimeout(done, 20);
});

_({
Expand Down Expand Up @@ -123,68 +128,146 @@ describe('rukorun', function(){
});
});

describe('when launching long running process', function(done){
var commands = "";
beforeEach(function(done){
command.write(JSON.stringify(_({
command: 'node',
args: ['fixture/server.js'],
env_vars: {
PORT: 1234,
PATH: process.env.PATH
}
}).defaults(payload)));

command.on('data', function(data){ commands+= data; });
io.pipe(process.stdout, {end: false});

io.once('data', function(data){
done();
describe('with node process', function(){
var nodePayload = _({
command: 'node',
env_vars: {
PORT: 1337,
PATH: process.env.PATH
}
}).defaults(payload);

describe('when launching web process', function(done){
var commands = "";
beforeEach(function(done){
command.write(JSON.stringify(_({
args: ['fixture/server.js'],
}).defaults(nodePayload)));

command.on('data', function(data){
commands+= data;
if(/{"type":"bound"}/.test(data)) done();
});
});

it('should kill process with SIGTERM when sending `stop`', function(done){
command.write(JSON.stringify({
type: 'stop'
}));

child.on('exit', function(code){
expect(commands).to.include('Stopping all processes with SIGTERM');
expect(commands).to.include('Process exited with status ');
done();
});
});
});

it('should kill process with SIGTERM when sending `stop`', function(done){
command.write(JSON.stringify({
type: 'stop'
}));
describe('when launching long booting process', function(done){
var commands = "";
beforeEach(function(done){
command.write(JSON.stringify(_({
args: ['fixture/setTimeout.js'],
}).defaults(nodePayload)));

child.on('exit', function(code){
expect(commands).to.include('Stopping all processes with SIGTERM');
expect(commands).to.include('Process exited with status ');
done();
command.on('data', function(data){ commands+= data; });

io.on('data', function(data){
done();
});
});

it('should kill process with R10 if not started after bootTimeout', function(done){
beforeEach(function(done){
setTimeout(done, 20);
});

child.on('exit', function(code){
expect(commands).to.include('Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch');
expect(commands).to.include('Stopping all processes with SIGKILL');
done();
});
});
});
});

describe('when launching catching signals processes', function(done){
var commands ="";
beforeEach(function(done){
command.write(JSON.stringify(_({
command: 'node',
args: ['fixture/chuck-norris.js'],
env_vars: {
PORT: 1234,
PATH: process.env.PATH
}
}).defaults(payload)));
describe('when launching a web process with a bad port', function(done){
var commands = "";
beforeEach(function(done){
command.write(JSON.stringify(_({
args: ['fixture/badPort.js'],
}).defaults(nodePayload)));

command.on('data', function(data){ commands+= data; });
command.on('data', function(data){ commands+= data; });

io.pipe(process.stdout, {end: false});
io.on('data', function(data){
done();
});
});

io.once('data', function(data){
done();
it('should kill process with R10 if not started after bootTimeout', function(done){
beforeEach(function(done){
setTimeout(done, 20);
});

child.on('exit', function(code){
expect(commands).to.include('Error R11 (Bad bind) -> Process bound to port 6666, should be 1337 (see environment variable PORT)');
expect(commands).to.include('Stopping all processes with SIGKILL');
done();
});
});
});

it('should kill process with SIGKILL when sending `exit`', function(done){
command.write(JSON.stringify({
type: 'stop'
}));
describe('when launching a web process with a bad host', function(done){
var commands = "";
beforeEach(function(done){
command.write(JSON.stringify(_({
args: ['fixture/badHost.js'],
}).defaults(nodePayload)));

child.on('exit', function(code){
expect(commands).to.include('Stopping all processes with SIGKILL');
done();
command.on('data', function(data){ commands+= data; });

io.on('data', function(data){
done();
});
});

it('should kill process with R10 if not started after bootTimeout', function(done){
beforeEach(function(done){
setTimeout(done, 20);
});

child.on('exit', function(code){
expect(commands).to.include('Error R11 (Bad bind) -> Process bound to host 1.0.0.127, should be 0.0.0.0');
expect(commands).to.include('Stopping all processes with SIGKILL');
done();
});
});
});


describe('when launching catching signals processes', function(done){
var commands ="";
beforeEach(function(done){
command.write(JSON.stringify(_({
args: ['fixture/chuck-norris.js'],
}).defaults(nodePayload)));

command.on('data', function(data){ commands+= data; });

command.on('data', function(data){
if(/{"type":"bound"}/.test(data)) done();
});
});

it('should kill process with SIGKILL when sending `exit`', function(done){
command.write(JSON.stringify({
type: 'stop'
}));

child.on('exit', function(code){
expect(commands).to.include('Stopping all processes with SIGKILL');
done();
});
});
});
});
Expand Down

0 comments on commit 7166f72

Please sign in to comment.