lots more initial work on the client side
diff --git a/invoker/init.sh b/invoker/init.sh
index 4cb5a50..74b69af 100755
--- a/invoker/init.sh
+++ b/invoker/init.sh
@@ -1,4 +1,4 @@
#!/bin/bash
-wsk package create owdbg -p broker "https://owdbg.mybluemix.net" -p action ""
+wsk package create owdbg -p broker "https://owdbg-broker.mybluemix.net" -p action ""
wsk action create owdbg/invoker owdbg-invoker.js
diff --git a/invoker/owdbg-invoker.js b/invoker/owdbg-invoker.js
index 920e067..d5af951 100644
--- a/invoker/owdbg-invoker.js
+++ b/invoker/owdbg-invoker.js
@@ -2,8 +2,13 @@
function main(params) {
return new Promise(function(resolve, reject) {
- console.log('Invoking ' + JSON.stringify(params) + ' ' + JSON.stringify(whisk));
+ console.log('Invoking ' + JSON.stringify(actualParameters));
+ var actualParameters = Object.assign({}, params);
+ delete actualParameters.action;
+ delete actualParameters.namespace;
+ delete actualParameters.onDone_trigger;
+
var opts = {
url: params.broker + '/invoke/begin',
method: 'POST',
@@ -15,7 +20,8 @@
key: whisk.getAuthKey(),
action: params.action,
namespace: params.namespace,
- params: params
+ onDone_trigger: params.onDone_trigger,
+ actualParameters: actualParameters
}
};
@@ -27,41 +33,45 @@
else console.log('OOPS1b ' + JSON.stringify(response) + ' ' + JSON.stringify(body));
reject(body);
} else {
- console.log('YUMMO ' + body);
+ console.log('YUMMO ' + JSON.stringify(body));
var activationId = body.activationId;
console.log('Ok, so far so good with activationId ' + activationId);
- var timer = setInterval(function() {
- request({
- url: params.broker + '/invoke/status/' + activationId,
- method: 'GET',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- 'AuthKey': whisk.getAuthKey()
- },
- }, function(err, response, body) {
- if (err || response.statusCode != 200) {
- if (err) console.log('OOPS2 ' + JSON.stringify(err));
- else console.log('OOPS2b ' + JSON.stringify(response));
- reject(body);
- } else {
- try {
- body = JSON.parse(body);
- console.log("Result? " + body.result + " " + body);
- if (body.result !== undefined) {
- clearInterval(timer);
- resolve(body);
- }
- } catch (e) {
- console.log("Could not parse result");
+ if (params.onDone_trigger) {
+ resolve({ status: "ok" });
+ } else {
+ var timer = setInterval(function() {
+ request({
+ url: actualParameters.broker + '/invoke/status/' + activationId,
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'AuthKey': whisk.getAuthKey()
+ },
+ }, function(err, response, body) {
+ if (err || response.statusCode != 200) {
+ if (err) console.log('OOPS2 ' + JSON.stringify(err));
+ else console.log('OOPS2b ' + JSON.stringify(response));
reject(body);
+ } else {
+ try {
+ // body = JSON.parse(body);
+ //console.log("Result? " + body.result + " " + body);
+ if (body.result !== undefined) {
+ clearInterval(timer);
+ resolve(body);
+ }
+ } catch (e) {
+ console.log("Could not parse result");
+ reject(body);
+ }
}
- }
- });
- }, 1000);
+ });
+ }, 1000);
+ }
}
});
});
}
-//main({'broker':'https://owdbg.mybluemix.net','action':'foo/bar15'})
+main({'broker':'https://owdbg.mybluemix.net','action':'foo/bar15'})
diff --git a/nodejs/client/lib/continuations.js b/nodejs/client/lib/continuations.js
new file mode 100644
index 0000000..40cb8b0
--- /dev/null
+++ b/nodejs/client/lib/continuations.js
@@ -0,0 +1,61 @@
+var request = require('request'),
+ uuid = require('uuid');
+
+module.exports = function exports(api) {
+ var module = {};
+
+ module.makeEchoChamber = function makeEchoChamber(key, namespace, next, nextOnErr) {
+ var names = {
+ rule: 'rule_' + uuid.v4(),
+ trigger: 'trigger_' + uuid.v4(),
+ action: 'action_' + uuid.v4()
+ }
+
+ makeTrigger(key, namespace, names.trigger,
+ makeAction.bind(undefined, key, namespace, names.action,
+ makeRule.bind(undefined,
+ { trigger: names.trigger, action: names.action },
+ key, namespace, names.rule, function allDone() {
+ next(names);
+ }, nextOnErr),
+ nextOnErr),
+ nextOnErr);
+
+ return names;
+ }
+
+ function makeEntity(type, body, key, namespace, name, next, nextOnErr) {
+ var opts = {
+ url: api.host + api.path + '/namespaces/' + encodeURIComponent(namespace) + '/' + type + 's/' + encodeURIComponent(name),
+ method: 'PUT',
+ json: true,
+ body: body || {},
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'basic ' + new Buffer(key).toString('base64')
+ },
+ };
+
+ request(opts, function(err, response, body) {
+ if (err || response.statusCode != 200) {
+ console.log('makeEchoChamber:Error ' + JSON.stringify(err, undefined, 4) + ' ' + (response && response.statusCode) + ' ' + JSON.stringify(body, undefined, 4));
+ nextOnErr();
+ } else {
+ next();
+ }
+ });
+ }
+
+ var echo = {
+ exec: {
+ kind: 'nodejs:default',
+ code: 'function main(params) { return params; }'
+ }
+ };
+
+ var makeTrigger = makeEntity.bind(undefined, 'trigger', undefined);
+ var makeAction = makeEntity.bind(undefined, 'action', echo);
+ var makeRule = makeEntity.bind(undefined, 'rule');
+
+ return module;
+};
diff --git a/nodejs/client/lib/debug-bootstrap.js b/nodejs/client/lib/debug-bootstrap.js
new file mode 100644
index 0000000..6b230cf
--- /dev/null
+++ b/nodejs/client/lib/debug-bootstrap.js
@@ -0,0 +1,23 @@
+var openwhisk = require('openwhisk'),
+ api = {
+ host: 'https://openwhisk.ng.bluemix.net',
+ path: '/api/v1'
+ };
+
+module.exports = function(key, namespace, triggerName) {
+ return function(main, actualParameters) {
+ var result = main(actualParameters || {});
+
+ var ow = openwhisk({
+ api: api.host + api.path,
+ api_key: key,
+ namespace: namespace
+ });
+ // debugger;
+
+ ow.triggers.invoke({
+ triggerName: triggerName,
+ params: result
+ });
+ };
+};
diff --git a/nodejs/client/lib/echo.js b/nodejs/client/lib/echo.js
new file mode 100644
index 0000000..240bf37
--- /dev/null
+++ b/nodejs/client/lib/echo.js
@@ -0,0 +1,3 @@
+function main(params) {
+ return params;
+}
diff --git a/nodejs/client/package.json b/nodejs/client/package.json
index c169339..9fb35e1 100644
--- a/nodejs/client/package.json
+++ b/nodejs/client/package.json
@@ -9,9 +9,16 @@
"author": "",
"license": "ISC",
"dependencies": {
+ "colors": "^1.1.2",
"expand-home-dir": "0.0.3",
+ "node-inspector": "^0.12.8",
+ "open": "0.0.5",
+ "openwhisk": "^2.1.3",
"prompt": "^1.0.0",
"properties-parser": "^0.3.1",
+ "request": "^2.75.0",
+ "tmp": "0.0.29",
+ "uuid": "^2.0.3",
"ws": "^1.1.1"
}
}
diff --git a/nodejs/client/wskdb b/nodejs/client/wskdb
new file mode 100755
index 0000000..5b84853
--- /dev/null
+++ b/nodejs/client/wskdb
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+node wskdb.js
diff --git a/nodejs/client/wskdb.js b/nodejs/client/wskdb.js
index 3cdcf35..e757e14 100644
--- a/nodejs/client/wskdb.js
+++ b/nodejs/client/wskdb.js
@@ -1,19 +1,31 @@
-var WebSocket = require('ws'),
+var fs = require('fs'),
+ tmp = require('tmp'),
+ open = require('open'),
+ path = require('path'),
+ spawn = require('child_process').spawn,
+ colors = require('colors'),
+ prompt = require('prompt'),
+ openwhisk = require('openwhisk'),
+ WebSocket = require('ws'),
expandHomeDir = require('expand-home-dir'),
- prompt = require('prompt');
+ propertiesParser = require('properties-parser'),
-var host = 'https://owdbg.mybluemix.net';
-var path = '/ws/client/register';
+ 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 uri = host + path;
-var ws = new WebSocket(uri);
-var key = require('properties-parser').read(expandHomeDir('~/.wskprops'))['AUTH'];
+var ws = new WebSocket(broker.host + broker.path);
-console.log(uri);
-console.log(key);
-
ws.on('open', function open() {
- console.log('CONNECTION OPEN');
+ console.log('The wskdb client is ready to debug!');
+
+ var key = propertiesParser.read(expandHomeDir('~/.wskprops'))['AUTH'];
ws.send(JSON.stringify({
type: 'init',
key: key
@@ -21,11 +33,12 @@
});
ws.on('close', function() {
- console.log('CONNECTION CLOSED ' + JSON.stringify(arguments));
+ console.log('Remote connection closed');
});
ws.on('message', function(data, flags) {
- console.log('MESSAGE ' + data + ' ||| ' + JSON.stringify(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.
@@ -34,37 +47,143 @@
var message = JSON.parse(data);
switch (message.type) {
case 'invoke':
- console.log('INVOKE');
- console.log(JSON.stringify(message, undefined, 4));
+ console.log('Debug session requested');
+ //console.log(JSON.stringify(message, undefined, 4));
- prompt.start();
- 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) {
+ var keepAlive = setInterval(function poke() {
ws.send(JSON.stringify({
- type: 'end',
+ type: 'keep-alive'
+ }));
+ }, 5000);
+
+ function done(err, result) {
+ console.log('Finishing up this debug session');
+
+ clearInterval(keepAlive);
+
+ ws.send(JSON.stringify({
+ type: err ? 'circuit-breaker' : 'end',
key: message.key,
activationId: message.activationId,
- result: values.result
+ 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.start();
+ 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 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() {
diff --git a/nodejs/client/wskinvoke.sh b/nodejs/client/wskinvoke.sh
new file mode 100755
index 0000000..0af255b
--- /dev/null
+++ b/nodejs/client/wskinvoke.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+NAMESPACE="`grep NAMESPACE ~/.wskprops | awk -F = '{print $2}'`"
+
+TRIGGER="`uuidgen`"
+ACTION="`uuidgen`"
+RULE="`uuidgen`"
+
+function allDone() {
+ (wsk trigger delete ${TRIGGER} >& /dev/null) &
+ (wsk action delete ${ACTION} >& /dev/null) &
+ wsk rule delete ${RULE} >& /dev/null
+ wait
+
+ echo
+ echo "Debug session complete"
+
+ exit
+}
+
+trap allDone INT
+
+wsk trigger create ${TRIGGER} >& /dev/null
+wsk action create ${ACTION} lib/echo.js >& /dev/null
+wsk rule create ${RULE} ${TRIGGER} ${ACTION} >& /dev/null
+
+wait
+
+wsk action invoke owdbg/invoker -p namespace "${NAMESPACE}" -p onDone_trigger ${TRIGGER} -p action $@
+
+echo -n "Your debugging session should now be active."
+
+while true; do
+ sleep 2
+
+ activationId=`wsk activation list | grep ${ACTION} | awk '{print $1}'`
+
+ if [ -n "$activationId" ]; then
+ wsk activation get $activationId
+ allDone
+ break
+ fi
+
+ echo -n "."
+done