From 67be5a7b1089f61ee79434f4257625367ccdb4c5 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Tue, 23 Aug 2016 17:23:20 -0700 Subject: [PATCH 1/7] NodeJS Function Run Container First cut of a function run container for NodeJS. So far, just the simplest stuff: loads a user function and routes requests to it. The user function is assumed to be in a file at a provided location. The fission runtime is expected to place the file there securely (exactly how we'll do that is TBD). Doesn't handle path template params, query strings, etc. Will also need to be extended later with some sort of hook for graceful shutdown. --- src/function-run/nodejs/Dockerfile | 7 +++ src/function-run/nodejs/package.json | 23 +++++++++ src/function-run/nodejs/server.js | 77 ++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 src/function-run/nodejs/Dockerfile create mode 100644 src/function-run/nodejs/package.json create mode 100644 src/function-run/nodejs/server.js diff --git a/src/function-run/nodejs/Dockerfile b/src/function-run/nodejs/Dockerfile new file mode 100644 index 00000000..8d8b54a9 --- /dev/null +++ b/src/function-run/nodejs/Dockerfile @@ -0,0 +1,7 @@ +# A docker image for the func container. + +FROM node:4-onbuild + +ADD server.js /usr/src/app/server.js + +EXPOSE 8888 diff --git a/src/function-run/nodejs/package.json b/src/function-run/nodejs/package.json new file mode 100644 index 00000000..e615126b --- /dev/null +++ b/src/function-run/nodejs/package.json @@ -0,0 +1,23 @@ +{ + "name": "fission-nodejs-runtime", + "version": "0.0.0", + "author": "Soam Vasani", + "contributors": [ + { + "name": "Soam Vasani", + "email": "soamvasani@platform9.com" + } + ], + "description": "NodeJS run container for the fission framework", + "engines": { + "node": ">=4.2.2" + }, + "dependencies": { + "express": "", + "minimist": "", + "body-parser": "", + + "require-from-string": "" + }, + "start": "node server.js --codepath '/fission/function' --port 8888" +} diff --git a/src/function-run/nodejs/server.js b/src/function-run/nodejs/server.js new file mode 100644 index 00000000..f623acdf --- /dev/null +++ b/src/function-run/nodejs/server.js @@ -0,0 +1,77 @@ +'use strict'; + +var fs = require('fs'); +var process = require('process'); +var express = require('express'); +var app = express(); +var bodyParser = require('body-parser'); + +// Command line opts +const argv = require('minimist')(process.argv.slice(1)); +if (!argv.codepath || !argv.port) { + console.log("Need --codepath and --port"); + process.exit(1); +} + +// User function. Starts out undefined. +let userFunction; + +// +// Specialize this server to a given user function. The user function +// is read from argv.codepath; it's expected to be placed there by the +// fission runtime. +// +function specialize(req, res) { + // Make sure we're a generic container. (No reuse of containers. + // Once specialized, the container remains specialized.) + if (userFunction) { + res.status(400).send("Not a generic container"); + return; + } + + // Read and load the code. It's placed there securely by the fission runtime. + try { + var startTime = process.hrtime(); + + const code = fs.readFileSync(argv.codepath).toString(); + userFunction = eval(code); + + var elapsed = process.hrtime(startTime); + console.log(`user code loaded in ${elapsed[0]}sec ${elapsed[1]}ns`); + } catch(e) { + console.log(`eval error: ${e}`); + res.status(500).send(JSON.stringify(e)); + return; + } + res.status(202).send(); +} + + +app.use(bodyParser.json()); +app.post('/specialize', specialize); + +// Generic route -- all http requests go to the user function. +app.all('/', function (req, res) { + if (!userFunction) { + res.status(500).send("Generic container: no requests supported"); + return; + } + const context = { + request: req, + response: res + // TODO: context should also have: URL template params, query string, ...anything else? + }; + function callback(status, body, headers) { + if (!status) + return; + if (headers) { + for (let name of Object.keys(headers)) { + res.set(name, headers[name]); + } + } + res.status(status).send(body); + } + userFunction(context, callback); +}); + +app.listen(argv.port); From ef04ff83cb636dfdfc618b1c0ae41516e8e21d9c Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 24 Aug 2016 11:55:40 -0700 Subject: [PATCH 2/7] Use const where appropriate. And log errors to stderr instead of stdout. --- src/function-run/nodejs/server.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/function-run/nodejs/server.js b/src/function-run/nodejs/server.js index f623acdf..92720b0b 100644 --- a/src/function-run/nodejs/server.js +++ b/src/function-run/nodejs/server.js @@ -1,15 +1,15 @@ 'use strict'; -var fs = require('fs'); -var process = require('process'); -var express = require('express'); -var app = express(); -var bodyParser = require('body-parser'); +const fs = require('fs'); +const process = require('process'); +const express = require('express'); +const app = express(); +const bodyParser = require('body-parser'); // Command line opts const argv = require('minimist')(process.argv.slice(1)); if (!argv.codepath || !argv.port) { - console.log("Need --codepath and --port"); + console.error("Need --codepath and --port"); process.exit(1); } @@ -39,7 +39,7 @@ function specialize(req, res) { var elapsed = process.hrtime(startTime); console.log(`user code loaded in ${elapsed[0]}sec ${elapsed[1]}ns`); } catch(e) { - console.log(`eval error: ${e}`); + console.error(`eval error: ${e}`); res.status(500).send(JSON.stringify(e)); return; } From 076b57c50a8950708c520c573c66c32956248cbe Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 24 Aug 2016 11:56:49 -0700 Subject: [PATCH 3/7] A very rudimentary test script We should have real tests with mocha/chai etc. Until then, use this little bash script. --- src/function-run/nodejs/test/test.js | 4 ++++ src/function-run/nodejs/test/test.sh | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/function-run/nodejs/test/test.js create mode 100755 src/function-run/nodejs/test/test.sh diff --git a/src/function-run/nodejs/test/test.js b/src/function-run/nodejs/test/test.js new file mode 100644 index 00000000..d3e16c38 --- /dev/null +++ b/src/function-run/nodejs/test/test.js @@ -0,0 +1,4 @@ +exports = function (context, callback) { + console.log("Test function"); + callback(200, "Hello, world!"); +} diff --git a/src/function-run/nodejs/test/test.sh b/src/function-run/nodejs/test/test.sh new file mode 100755 index 00000000..8ab05116 --- /dev/null +++ b/src/function-run/nodejs/test/test.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# TODO placeholder until we have better tests :) + +set +x +set -e + +DIR=$(dirname $0) + +echo "-- Starting server" +node $DIR/../server.js --codepath $DIR/test.js --port 8888 & +sleep 2 + +echo "-- Specializing" +curl -f -X POST http://localhost:8888/specialize + +echo "-- Running user function" +curl -f http://localhost:8888 ; echo + +echo "-- Cleanup" +kill %1 + From 337843f5bce3d6c9e918386a3d55db93b1b01af3 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 24 Aug 2016 13:10:17 -0700 Subject: [PATCH 4/7] Test script bugfix --- src/function-run/nodejs/test/test.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/function-run/nodejs/test/test.sh b/src/function-run/nodejs/test/test.sh index 8ab05116..e8ab274b 100755 --- a/src/function-run/nodejs/test/test.sh +++ b/src/function-run/nodejs/test/test.sh @@ -9,6 +9,11 @@ DIR=$(dirname $0) echo "-- Starting server" node $DIR/../server.js --codepath $DIR/test.js --port 8888 & +function cleanup() { + echo "-- Cleanup" + kill %1 +} +trap cleanup EXIT sleep 2 echo "-- Specializing" @@ -16,7 +21,3 @@ curl -f -X POST http://localhost:8888/specialize echo "-- Running user function" curl -f http://localhost:8888 ; echo - -echo "-- Cleanup" -kill %1 - From 7a316b14e61b546cde94445509a4eb5b1d6f3d61 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 24 Aug 2016 13:10:35 -0700 Subject: [PATCH 5/7] Use require instead of eval to load user code --- src/function-run/nodejs/server.js | 9 +++------ src/function-run/nodejs/test/test.js | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/function-run/nodejs/server.js b/src/function-run/nodejs/server.js index 92720b0b..059189f4 100644 --- a/src/function-run/nodejs/server.js +++ b/src/function-run/nodejs/server.js @@ -32,14 +32,11 @@ function specialize(req, res) { // Read and load the code. It's placed there securely by the fission runtime. try { var startTime = process.hrtime(); - - const code = fs.readFileSync(argv.codepath).toString(); - userFunction = eval(code); - + userFunction = require(argv.codepath); var elapsed = process.hrtime(startTime); - console.log(`user code loaded in ${elapsed[0]}sec ${elapsed[1]}ns`); + console.log(`user code loaded in ${elapsed[0]}sec ${elapsed[1]/1000000}ms`); } catch(e) { - console.error(`eval error: ${e}`); + console.error(`user code load error: ${e}`); res.status(500).send(JSON.stringify(e)); return; } diff --git a/src/function-run/nodejs/test/test.js b/src/function-run/nodejs/test/test.js index d3e16c38..fc8fca57 100644 --- a/src/function-run/nodejs/test/test.js +++ b/src/function-run/nodejs/test/test.js @@ -1,4 +1,4 @@ -exports = function (context, callback) { +module.exports = function (context, callback) { console.log("Test function"); callback(200, "Hello, world!"); } From 0ea5e2b05e5d7db605ac0e4c4e723039fcff4840 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 24 Aug 2016 13:18:43 -0700 Subject: [PATCH 6/7] Remove unused dependency And we aren't using the 'start' command either. --- src/function-run/nodejs/package.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/function-run/nodejs/package.json b/src/function-run/nodejs/package.json index e615126b..fcd3f671 100644 --- a/src/function-run/nodejs/package.json +++ b/src/function-run/nodejs/package.json @@ -15,9 +15,6 @@ "dependencies": { "express": "", "minimist": "", - "body-parser": "", - - "require-from-string": "" - }, - "start": "node server.js --codepath '/fission/function' --port 8888" + "body-parser": "" + } } From 12f6a9335280835c510a671c0f52e87c15559f8b Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Wed, 24 Aug 2016 13:49:45 -0700 Subject: [PATCH 7/7] Test other http verbs: get, put, post, delete, options, trace, head. --- src/function-run/nodejs/test/test.js | 5 +++-- src/function-run/nodejs/test/test.sh | 14 +++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/function-run/nodejs/test/test.js b/src/function-run/nodejs/test/test.js index fc8fca57..288ef627 100644 --- a/src/function-run/nodejs/test/test.js +++ b/src/function-run/nodejs/test/test.js @@ -1,4 +1,5 @@ module.exports = function (context, callback) { - console.log("Test function"); - callback(200, "Hello, world!"); + console.log("Test function entered"); + callback(200, "Hello, world!\n"); + console.log("Test function exit"); } diff --git a/src/function-run/nodejs/test/test.sh b/src/function-run/nodejs/test/test.sh index e8ab274b..49907e64 100755 --- a/src/function-run/nodejs/test/test.sh +++ b/src/function-run/nodejs/test/test.sh @@ -3,7 +3,7 @@ # TODO placeholder until we have better tests :) set +x -set -e +set -e DIR=$(dirname $0) @@ -17,7 +17,15 @@ trap cleanup EXIT sleep 2 echo "-- Specializing" -curl -f -X POST http://localhost:8888/specialize +curl -f -X POST http://localhost:8888/specialize echo "-- Running user function" -curl -f http://localhost:8888 ; echo +curl -f -X GET http://localhost:8888 +curl -f -X POST http://localhost:8888 +curl -f -X PUT http://localhost:8888 +curl -f -X DELETE http://localhost:8888 +curl -f -X TRACE http://localhost:8888 +curl -f -X OPTIONS http://localhost:8888 + +# -I causes curl to make a HEAD request. +curl -f -I http://localhost:8888