From 67be5a7b1089f61ee79434f4257625367ccdb4c5 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Tue, 23 Aug 2016 17:23:20 -0700 Subject: [PATCH] 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);