blob: c99ea2706a25390882a7a67f7b7596b104fcc045 [file] [log] [blame]
var fs = require('fs'),
tmp = require('tmp'),
open = require('open'),
path = require('path'),
repl = require('./lib/repl').repl,
spawn = require('child_process').spawn,
colors = require('colors'),
columnify = require('columnify'),
WebSocket = require('ws'),
expandHomeDir = require('expand-home-dir'),
propertiesParser = require('properties-parser'),
api = {
host: 'https://openwhisk.ng.bluemix.net',
path: '/api/v1'
},
broker = {
host: 'https://owdbg-broker.mybluemix.net',
path: '/ws/client/register'
},
continuations = require('./lib/continuations')(api);
var ws = new WebSocket(broker.host + broker.path);
ws.on('open', function open() {
console.log('Welcome to the OpenWhisk Debugger');
console.log();
var wskprops = propertiesParser.read(expandHomeDir('~/.wskprops'));
var key = wskprops['AUTH'];
ws.send(JSON.stringify({
type: 'init',
key: key
}));
var keepAlive = setInterval(function poke() {
ws.send(JSON.stringify({
type: 'keep-alive'
}));
}, 5000);
repl(wskprops);
});
ws.on('close', function() {
console.log('Remote connection closed');
});
ws.on('message', function(data, flags) {
//console.log('MESSAGE ' + data + ' ||| ' + JSON.stringify(flags));
//
// flags.binary will be set if a binary data is received.
// flags.masked will be set if the data was masked.
//
try {
var message = JSON.parse(data);
switch (message.type) {
case 'invoke':
console.log('Debug session requested');
//console.log(JSON.stringify(message, undefined, 4));
function done(err, result) {
console.log('Finishing up this debug session');
ws.send(JSON.stringify({
type: err ? 'circuit-breaker' : 'end',
key: message.key,
activationId: message.activationId,
result: result
}));
//ws.close();
}
function next(echoChamberNames) {
if (message.action && message.action.exec && message.action.exec.kind.indexOf('nodejs') >= 0) {
debugNodeJS(message, ws, echoChamberNames, done);
} else {
ws.send(JSON.stringify({
type: 'circuit-breaker',
key: message.key,
activationId: message.activationId,
}));
}
}
var nextOnErr = done.bind(undefined, true);
// console.log("OOOOOOOOOOOOOOOOOO " + message.onDone_trigger + " " + JSON.stringify(message, undefined, 4))
if (message.onDone_trigger) {
next({ trigger: message.onDone_trigger });
} else {
continuations.makeEchoChamber(message.key, message.action.namespace, next, nextOnErr);
}
break;
}
} catch (e) {
console.log(e);
}
});
function debugDebug(message, ws, done) {
prompt.get({
name: 'result', description: 'Return value',
conform: function(result) {
try {
JSON.parse(result);
return true;
} catch (e) {
console.log('NOPE ' + result);
return false;
}
}
}, function(err, values) {
done(values.result);
});
}
function debugNodeJS(message, ws, echoChamberNames, done) {
var code = message.action.exec.code;
var r = new RegExp(/main[\s]*\([^\)]*\)/)
var startOfMethodBody = code.search(r);
if (startOfMethodBody >= 0) {
var paren = code.indexOf('{', startOfMethodBody);
code = code.substring(0, paren + 1) + '\n // This is your main method\n // Click continue, and you will stop here\n debugger;\n' + code.substring(paren + 1);
}
/* var bootstrap = '\n\n\nvar result = main.apply(undefined, ' + JSON.stringify([message.actualParameters || {}]) + ');';
// fire our echo chamber trigger when the code is done
bootstrap += '\n\nvar openwhisk = require(\'openwhisk\');\n';
bootstrap += 'ow = openwhisk({api: \'' + api.host + api.path + '\', api_key: \'' + message.key + '\', namespace: \'' + message.action.namespace + '\' });\n';
bootstrap += 'ow.triggers.invoke({ triggerName: \'' + echoChamberNames.trigger + '\', params: result });\n';*/
code += '\n\n//\n'
code += '// Welcome to the OpenWhisk debugger.\n';
code += '//\n';
code += '// To proceed with debugging, press the continue => button.\n';
code += '// The first breakpoint will be in your main method\n';
code += '//\n';
code += '\n\nvar bootstrap = require(\'debug-bootstrap\')(\'' + message.key + '\', \'' + message.action.namespace + '\', \'' + echoChamberNames.trigger + '\');\nbootstrap(main, ' + JSON.stringify(message.actualParameters || {}) + ');';
tmp.file(function onTempFileCreation(err, tmpFilePath, fd, tmpfileCleanupCallback) {
// console.log('TMP ' + tmpFilePath);
fs.write(fd, code, 0, 'utf8', function onFileWriteCompletion(err, written, string) {
var env = Object.assign({}, process.env);
env['NODE_PATH'] = path.join(process.cwd(), 'node_modules')
+ ':' + path.join(process.cwd(), 'lib');
var spawnOpts = {
cwd: process.cwd(),
//stdio: ['inherit', 'pipe', 'pipe'],
env: env
};
//console.log('SPAWN ' + JSON.stringify(spawnOpts, undefined, 4));
//var child = spawn('node', ['debug', tmpFilePath], spawnOpts);
var child = spawn(path.join('node_modules', '.bin', 'node-debug'), [tmpFilePath], spawnOpts);
/*var child = spawn('node', ['--debug', '--debug-brk', tmpFilePath], spawnOpts);
console.log('SPAWN2');
var child2 = spawn(path.join('node_modules', '.bin', 'node-inspector'), spawnOpts);
console.log('OPEN');
var child3 = open('http://127.0.0.1:8080/?port=5858', 'Google Chrome');*/
/*child.stderr.on('data', function(data) {
console.log(data);
});*/
console.log("");
console.log("");
console.log("\tVisit " + "http://127.0.0.1:8080/?port=5858".underline.blue + " in the " + "Chrome".red + " browser that just popped up");
console.log("\tClose that browser tab to complete your debugging session".bold);
console.log("");
console.log("");
function cleanUpSubprocesses(err, stdout, stderr) {
/*console.log('ERR: ' + err);
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);*/
tmpfileCleanupCallback();
done();
}
child.on('close', cleanUpSubprocesses);
});
});
}
/*
sending binary data
ws.on('open', function open() {
var array = new Float32Array(5);
for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}
ws.send(array, { binary: true, mask: true });
});
*/
process.on('exit', function onExit() {
try {
console.log("Goodbye!".red);
ws.send(JSON.stringify({
type: 'disconnect'
}, function ack() {
ws.close();
}));
} catch (e) {
}
});