blob: 1b8166a2467f1785cfbbca96660cbe8ea348f0be [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(sadovsky): Rename this file service-runner.js.
var debug = require('debug')('run-services');
var EE = require('events').EventEmitter;
var extend = require('xtend');
var inherits = require('util').inherits;
var mkdirp = require('mkdirp');
var parallel = require('run-parallel');
var path = require('path');
var service = require('./service');
module.exports = Runner;
/**
* var runner = Runner(names=[])
*
* Emits: start, stop, error
*
* @param {Array} names of the vanadium services to run.
* @returns {Runner}
*/
function Runner(names) {
if (!(this instanceof Runner)) {
return new Runner(names);
}
// TODO(sadovsky): Make these fields private.
var runner = this;
EE.call(runner);
// Env vars produced by servicerunner job. These are made visible to clients
// in case they want to pass them to other subprocesses.
runner.env = {};
runner._names = names;
runner._services = [];
// Used to make start() and stop() idempotent.
runner._startCalled = false;
runner._stopCalled = false;
}
inherits(Runner, EE);
Runner.prototype.start = function(cb) {
var runner = this;
if (runner._startCalled) {
debug('warning: start called multiple times');
return;
}
runner._startCalled = true;
debug('starting core services...');
runner._setup(function() {
debug('core services ready; starting additional services...');
var services = runner._names.map(runner._add.bind(runner));
// Note, we don't map over runner._services because that includes
// servicerunner.
var jobs = services.map(createStartWorker);
parallel(jobs, function(err) {
debug('started all services');
runner.emit('start');
cb(err);
});
});
return runner;
};
Runner.prototype._add = function(name) {
var runner = this;
var env = extend(process.env, runner.env);
var s = service(name, env);
s.on('error', function(err) {
debug('error detected, stopping all services');
runner.emit('error', err);
runner.stop();
});
runner._services.push(s);
return s;
};
// Creates a temporary directory for output, generates a principal, starts core
// services (mount table, proxyd, wsprd), writes various vars to runner.env,
// then calls cb.
Runner.prototype._setup = function(cb) {
var runner = this;
mkdirp(path.resolve('tmp/log'), createCredentials);
function createCredentials(err) {
if (err) {
return runner.emit('error', err);
}
runner.env.V23_CREDENTIALS = path.resolve('tmp/test-credentials');
service('principal')
.exec('create -with-passphrase=false -overwrite ' +
runner.env.V23_CREDENTIALS + ' test',
function(err, stdout, stderr) {
if (err) {
return runner.emit('error', err);
}
stdout.on('close', startCoreServices);
});
}
function startCoreServices() {
runner
._add('servicerunner')
.on('vars', function(vars) {
runner.env = extend(runner.env, {
IDENTITYD: vars.TEST_IDENTITYD_NAME + '/google',
IDENTITYD_BLESSING_URL:
vars.TEST_IDENTITYD_HTTP_ADDR + '/auth/blessing-root',
V23_NAMESPACE: vars.MT_NAME,
PROXY_ADDR: vars.PROXY_NAME,
WSPR_ADDR: vars.WSPR_ADDR
});
console.log('Tests running with environment: ', runner.env);
})
.on('ready', function() {
debug('core services running');
cb();
})
.spawn();
}
};
Runner.prototype.stop = function(cb) {
var runner = this;
if (runner._stopCalled) {
var errText = 'stop called multiple times';
debug(errText);
if (cb) {
cb(new Error(errText));
}
return;
}
runner._stopCalled = true;
cb = cb || function() {};
debug('stopping...');
// Remove all error listeners before killing any services so that services
// don't complain when other services they depend on are shut down.
runner._services.map(function(s) {
s.removeAllListeners('error');
});
var jobs = runner._services.map(createStopWorker);
parallel(jobs, function(err) {
debug('stopped');
runner.emit('stop');
cb(err);
});
};
function createStartWorker(service) {
return function start(cb) {
debug('spawning: %s', service.name);
service.on('ready', function ready() {
debug('ready: %s', service.name);
cb();
});
service.spawn();
};
}
function createStopWorker(service) {
return function stop(cb) {
debug('exiting %s', service.name);
service.on('exit', cb);
service.kill('SIGTERM');
};
}