From 4a62ff5d3d58c18fe6b40877c015da500646a06f Mon Sep 17 00:00:00 2001 From: Cameron Kenneth Knight Date: Thu, 21 Apr 2011 18:20:53 -0500 Subject: [PATCH] Add 'env' to the apps database, which is a String-to-String Object map that represents the environment variables that an app is to be run with. Add NODE_ENV="production" as a default env variable, but allow it to be overridden. Add RESTful /env API for managing environment variables. --- app.js | 12 +++++ lib/app.js | 86 +++++++++++++++++++++++++++++++++- scripts/launch_chrooted_app.js | 16 +++++-- 3 files changed, 109 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 343bce1a..b09ba23b 100644 --- a/app.js +++ b/app.js @@ -105,6 +105,18 @@ myapp.delete('/gitreset', middle.authenticate, middle.authenticate_app, app.gitr // curl -u "testuser:123" -d "appname=test" http://localhost:4001/applogs myapp.get('/applogs/:appname', middle.authenticate, middle.authenticate_app, app.logs); +// Retrieve information about or update a node app's ENV variables +// This fulfills all four RESTful verbs. +// GET will retrieve the list of all keys. +// PUT will either create or update. +// DELETE will delete the key if it exists. +// curl -u GET -u "testuser:123" -d "appname=test" http://localhost:4001/env +// curl -u PUT -u "testuser:123" -d "appname=test&key=NODE_ENV&value=production" http://localhost:4001/env +// curl -u DELETE -u "testuser:123" -d "appname=test&key=NODE_ENV" http://localhost:4001/env +myapp.get('/env', middle.authenticate, middle.authenticate_app, app.env_get); +myapp.put('/env', middle.authenticate, middle.authenticate_app, app.env_put); +myapp.delete('/env', middle.authenticate, middle.authenticate_app, app.env_delete); + // APP NPM Handlers var npm = require('./lib/npm'); // curl -X POST -u "testuser:123" -d "appname=test&package=express" http://localhost:4001/appnpm diff --git a/lib/app.js b/lib/app.js index 6a82e5a9..b43b9549 100644 --- a/lib/app.js +++ b/lib/app.js @@ -304,7 +304,8 @@ module.exports = { username: user._id, repo_id: repo_id, running: false, - pid: 'unknown' + pid: 'unknown', + env: {} }, function (err, resp) { if (err) { res.writeHead(500, { 'Content-Type': 'application/json' }); @@ -353,9 +354,89 @@ module.exports = { res.send({status: "failure", message: "app exists"}); } }); + }, + env_get: function(req, res, next) { + var appname = req.body.appname.toLowerCase(); + var db = lib.get_couchdb_database('apps'); + db.get(appname, function (err, appdoc) { + if (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({status: "failure", message: err.error + ' - ' + err.reason}) + '\n'); + } else { + var start = req.body.start; + db.get(appname, function (err, doc) { + if (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({status: "failure", message: err.error + ' - ' + err.reason}) + '\n'); + } else { + res.send({status: "success", message: doc.env || {}}); + } + }); + } + }); + }, + env_put: function(req, res, next) { + var appname = req.body.appname.toLowerCase(); + var db = lib.get_couchdb_database('apps'); + var key = req.body.key, + value = req.body.value; + if (!key || !value) { + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.write(JSON.stringify({status: "false", message: "Must specify both key and value."}) + "\n"); + res.end(); + return; + } + db.get(appname, function (err, appdoc) { + if (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({status: "failure", message: err.error + ' - ' + err.reason}) + '\n'); + } else { + env_update(res, db, appname, appdoc, key, value); + } + }); + }, + env_delete: function(req, res, next) { + var appname = req.body.appname.toLowerCase(); + var db = lib.get_couchdb_database('apps'); + var key = req.body.key; + if (!key) { + res.writeHead(400, { 'Content-Type': 'application/json' }); + res.write(JSON.stringify({status: "false", message: "Must specify key."}) + "\n"); + res.end(); + return; + } + db.get(appname, function (err, appdoc) { + if (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({status: "failure", message: err.error + ' - ' + err.reason}) + '\n'); + } else { + env_update(res, db, appname, appdoc, key, undefined); + } + }); } } +var env_update = function(res, db, appname, appdoc, key, value) { + var env = {}; + if (appdoc.env) { + Object.keys(appdoc.env).forEach(function (k) { + env[k] = appdoc.env[k]; + }); + } + if (value !== undefined) { + env[key] = value; + } else { + delete env[key]; + } + db.merge(appname, {env: env}, function(err, resp) { + if (err) { + res.writeHead(500, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({status: "failure", message: err.error + ' - ' + err.reason}) + '\n'); + } else { + res.send({status: "success", message: value === undefined ? ("DELETE " + key) : (key + "=" + value)}); + } + }); +}; var force_stop = function(repo_id, callback) { console.log('Forcing stop for: ', repo_id); @@ -433,7 +514,8 @@ var app_start = function (repo_id, callback) { start: app.start, port: app.port, ip: '127.0.0.1', - name: doc.appname + name: doc.appname, + env: app.env || {} }; console.log('Checking: ', app_home); if (!path.existsSync(app_home)) { diff --git a/scripts/launch_chrooted_app.js b/scripts/launch_chrooted_app.js index a684d3e1..528c7193 100755 --- a/scripts/launch_chrooted_app.js +++ b/scripts/launch_chrooted_app.js @@ -82,10 +82,20 @@ daemon.daemonize(path.join('.nodester', 'logs', 'daemon.log'), path.join('.nodes sandbox.process.installPrefix = '/'; sandbox.process.ARGV = ['node', config.start]; sandbox.process.argv = sandbox.process.ARGV; - sandbox.process.env = sandbox.process.ENV = { - 'app_port': app_port, - 'app_host': app_host + var env = sandbox.process.env = sandbox.process.ENV = { + // defaults which can be overriden + NODE_ENV: "production" }; + + if (config.env) { + Object.keys(config.env).forEach(function (key) { + env[key] = String(config.env[key]); + }); + } + + // environment variables which cannot be overriden by config. + env.app_port = app_port; + env.app_host = app_host; sandbox.process.mainModule = sandbox.module; sandbox.process.kill = function () { return 'process.kill is disabled' }; sandbox.process.stdout.write = sandbox.console.warn = sandbox.console.error = function (args) {