load services

This commit is contained in:
AJ ONeal 2015-11-18 11:44:22 +00:00
parent 96b7c9bb65
commit 78178eb43d
6 changed files with 163 additions and 27 deletions

View File

@ -78,6 +78,10 @@ cluster.on('online', function (worker) {
info.conf.ipcKey = conf.ipcKey;
info.conf.memstoreSock = conf.memstoreSock;
info.conf.sqlite3Sock = conf.sqlite3Sock;
// TODO get this from db config instead
var config = require('../config');
info.conf.primaryNameserver = config.primaryNameserver;
info.conf.nameservers = config.nameservers;
worker.send(info);
});
}

View File

@ -20,6 +20,10 @@ module.exports.create = function (conf, deps, app) {
return new PromiseA(function (resolve, reject) {
try {
// TODO dynamic requires are a no-no
// can we statically generate a require-er? on each install?
// module.exports = { {{pkgpath}}: function () { return require({{pkgpath}}) } }
// requirer[pkgpath]()
route.route = require(pkgpath).create(conf, deps, app);
} catch(e) {
reject(e);

113
lib/services-loader.js Normal file
View File

@ -0,0 +1,113 @@
'use strict';
module.exports.create = function (conf, deps) {
var PromiseA = deps.Promise;
function loadService(node) {
var path = require('path');
return new PromiseA(function (resolve) {
// process.nextTick runs at the end of the current event loop
// we actually want time to pass so that potential api traffic can be handled
setTimeout(function () {
var servicepath = path.join(conf.servicespath, node);
var pkg;
try {
// TODO no package should be named package.json
pkg = require(servicepath + '/package.json');
resolve({
pkg: pkg
, name: node
, service: require(servicepath)
});
return;
} catch(e) {
// TODO report errors to admin console
// TODO take sha256sum of e.stack and store in db with tick for updatedAt
console.error("[Service] could not require service '" + servicepath + "'");
console.error(e.stack);
//services.push({ error: e });
resolve(null);
return;
}
}, 1);
});
}
function loadServices() {
var fs = PromiseA.promisifyAll(require('fs'));
// deps : { memstore, sqlstores, clientSqlFactory, systemSqlFactory }
// XXX this is a no-no (file system access in a worker, cannot be statically analyzed)
// TODO regenerate a static file of all requires on each install
// TODO read system config db to find which services auto-start
// TODO allow certain apis access to certain services
return fs.readdirAsync(conf.servicespath).then(function (nodes) {
var promise = PromiseA.resolve();
var services = [];
nodes.forEach(function (node) {
promise = promise.then(function () {
return loadService(node).then(function (srv) {
if (!srv) {
return;
}
services.push(srv);
});
});
});
return promise.then(function () {
return services;
});
});
}
function startService(srv) {
return new PromiseA(function (resolve) {
// process.nextTick runs at the end of the current event loop
// we actually want time to pass so that potential api traffic can be handled
setTimeout(function () {
try {
PromiseA.resolve(srv.service.create(conf, deps)).then(resolve, function (e) {
console.error("[Service] couldn't promise service");
console.error(e.stack);
resolve(null);
});
return;
} catch(e) {
console.error("[Service] couldn't start service");
console.error(e.stack);
resolve(null);
return;
}
}, 1);
});
}
function startServices(services) {
var promise = PromiseA.resolve();
var servicesMap = {};
services.forEach(function (srv) {
promise = promise.then(function () {
return startService(srv).then(function (service) {
if (!service) {
// TODO log
return null;
}
srv.service = service;
servicesMap[srv.name] = srv;
});
});
});
return promise.then(function () {
return servicesMap;
});
}
return loadServices().then(startServices);
};

View File

@ -11,6 +11,7 @@ module.exports.create = function (webserver, info, state) {
var express = require('express-lazy');
var app = express();
var apiHandler;
var Services;
var memstore;
var sqlstores = {};
var models = {};
@ -110,6 +111,8 @@ module.exports.create = function (webserver, info, state) {
app.use('/', caddyBugfix);
return PromiseA.all([
// TODO security on memstore
// TODO memstoreFactory.create
cstore.create({
sock: info.conf.memstoreSock
, connect: info.conf.memstoreSock
@ -146,14 +149,28 @@ module.exports.create = function (webserver, info, state) {
return require('../lib/schemes-config').create(sqlstores.config).then(function (tables) {
models.Config = tables;
return models.Config.Config.get().then(function (vhostsMap) {
/*
// todo getDomainInfo
var utils = require('./utils');
results.domains.forEach(function (domain) {
utils.getDomainInfo(domain.id);
});
*/
// TODO the core needs to be replacable in one shot
// rm -rf /tmp/walnut/; tar xvf -C /tmp/walnut/; mv /srv/walnut /srv/walnut.{{version}}; mv /tmp/walnut /srv/
// this means that any packages must be outside, perhaps /srv/walnut/{boot,core,packages}
var apiConf = {
apppath: '../packages/apps/'
, apipath: '../packages/apis/'
, servicespath: path.join(__dirname, '..', 'packages', 'services')
, vhostsMap: vhostsMap
, server: webserver
, externalPort: info.conf.externalPort
, primaryNameserver: info.conf.primaryNameserver
, nameservers: info.conf.nameservers
, apiPrefix: '/api'
};
Services = require('./services-loader').create(apiConf, {
memstore: memstore
, sqlstores: sqlstores
, clientSqlFactory: clientFactory
, systemSqlFactory: systemFactory
, Promise: PromiseA
});
function handleApi(req, res, next) {
var myApp;
@ -183,29 +200,22 @@ module.exports.create = function (webserver, info, state) {
return;
}
// apiHandler = require('./vhost-server').create(info.localPort, vhostsdir).create(webserver, app)
// apiHandler = require('./vhost-server').create(info.conf.localPort, vhostsdir).create(webserver, app)
myApp = express();
if (app.get('trust proxy')) {
myApp.set('trust proxy', app.get('trust proxy'));
}
apiHandler = require('./api-server').create(
{ apppath: '../packages/apps/'
, apipath: '../packages/apis/'
, vhostsMap: vhostsMap
, server: webserver
, externalPort: info.externalPort
, apiPrefix: '/api'
}
, { app: myApp
, memstore: memstore
, sqlstores: sqlstores
, clientSqlFactory: clientFactory
, systemSqlFactory: systemFactory
//, handlePromise: require('./lib/common').promisableRequest;
//, handleRejection: require('./lib/common').rejectableRequest;
//, localPort: info.localPort
}
).api;
apiHandler = require('./api-server').create(apiConf, {
app: myApp
, memstore: memstore
, sqlstores: sqlstores
, clientSqlFactory: clientFactory
, systemSqlFactory: systemFactory
//, handlePromise: require('./lib/common').promisableRequest;
//, handleRejection: require('./lib/common').rejectableRequest;
//, localPort: info.conf.localPort
, Promise: PromiseA
}, Services).api;
apiHandler(req, res, next);
}

View File

@ -86,6 +86,7 @@
"mime-db": "^1.8.0",
"mime-types": "^2.0.10",
"ms": "^0.7.0",
"native-dns": "^0.7.0",
"negotiator": "^0.5.1",
"node-pre-gyp": "^0.6.4",
"node-uuid": "^1.4.4",

View File

@ -22,5 +22,9 @@ Math.random = function () {
if (cluster.isMaster) {
require('./boot/master');
} else {
/*
alternately we could use this and then check require.main
cluster.setupMaster({ exec : "app.js", });
*/
require('./boot/worker').create(null);
}