berlioz
Version:
Berlioz - cloud deployment and migration services
468 lines (415 loc) • 16.3 kB
JavaScript
const Promise = require('the-promise');
const _ = require('the-lodash');
const Loader = require('./berlioz-common/loader');
const Extractor = require('./extractor');
const CommonErrors = require('./berlioz-common/errors');
const Errors = require('./tools/errors');
const Context = require('./tools/context');
const Storage = require('./tools/storage');
const ConfigRegistry = require('./tools/config-registry');
const BerliozClient = require('./tools/berlioz-client');
const AccountDataProvider = require('./tools/account-data-provider');
const GcpHelper = require('./tools/gcp-helper');
const VorpalInterface = require('./tools/vorpal-interface');
const Screen = require('./tools/screen');
const LocalDeployer = require('./tools/local-deployer');
const Shell = require('./tools/shell');
const Docker = require('./tools/docker');
const Waiter = require('./tools/waiter');
const PlantUml = require('./tools/plantuml');
const Environment = require('./tools/environment');
const Autocomplete = require('./autocomplete');
process.on('unhandledRejection', (reason, p) => {
console.log('[unhandledRejection] Unhandled Rejection at:', p, 'reason:', reason);
console.log(reason);
console.log(p);
});
class Starter
{
constructor(logger, rootDir)
{
this._logger = logger;
this._rootDir = rootDir;
this._commands = [];
this._screen = new Screen();
this._context = new Context(this._logger.sublogger('Context'));
this._storage = new Storage(this._logger.sublogger('Storage'), this._screen);
this._environment = new Environment(this._logger.sublogger('Environment'), this._storage);
this._configRegistry = new ConfigRegistry(this._logger.sublogger('ConfigRegistry'), this._storage);
this._client = new BerliozClient(this._logger.sublogger('Client'), this._screen, this._configRegistry, this._environment);
this._autocomplete = new Autocomplete(this._logger, this._client);
this._interface = new VorpalInterface(this._logger,
this._screen,
this._executeCommand.bind(this),
this._autocompleteProcessor.bind(this));
}
run(commandStr)
{
return Promise.resolve()
.then(() => this._extractCommands())
.then(() => {
if (commandStr.length == 0) {
return this._interface.interactive();
} else {
return this._interface.exec(commandStr);
}
})
.catch(reason => {
this._handleCommandError(commandStr, reason, null);
})
;
}
_extractCommands()
{
var extractor = new Extractor(this._logger, this._configRegistry.repoStore, this._acceptCommand.bind(this), this._environment);
return extractor.process();
}
_acceptCommand(command)
{
try {
var commandModule = require(command.includePath);
if (!commandModule.arguments) {
commandModule.arguments = []
}
if (!commandModule.flags) {
commandModule.flags = []
}
if (commandModule.useProject) {
commandModule.arguments.push({
name: "pathoverride",
optional: true
})
commandModule.arguments.push({
name: "ignoredirs",
optional: true
})
}
if (commandModule.useWaiter) {
if (!commandModule.skipWaitFlag) {
commandModule.flags.push("wait");
}
commandModule.arguments.push({
name: "timeout",
optional: true
})
}
if (commandModule.useClient) {
commandModule.flags.push("skip-ssl");
}
commandModule.flags.push("wide-screen");
commandModule.flags.push("narrow-screen");
this._interface.accept(command, commandModule);
this._commands.push(command);
} catch (e) {
this._logger.error('Failed accepting command.');
this._logger.exception(e);
}
}
_triggerInteractiveShell()
{
this._logger.verbose('Triggering interactive shell...');
this._interface.interactive();
}
_executeCommand(info, commandModule, args)
{
this._logger.info('Running command: %s...', info.command);
this._logger.info('ARGS: ', args);
var commandResult = null;
try {
var commandParams = {
host: this,
rootDir: this._rootDir,
args: args.options,
storage: this._storage,
config: this._configRegistry,
logger: this._logger,
screen: this._screen,
parseDefinitions: this.parseDefinitions.bind(this),
Promise: Promise,
_: _
};
if (commandParams.args["wide-screen"]) {
this._screen.setupScreen(120);
} else if (commandParams.args["narrow-screen"]) {
this._screen.setupScreen(80);
}
commandParams.inputError = (msg) => {
throw new Errors.Input(msg);
}
commandParams.genericError = (msg) => {
throw new CommonErrors.Generic(msg);
}
if (commandModule.canRunCommand) {
commandParams.vorpal = this._interface;
}
commandModule.shell = new Shell(this._logger.sublogger('Shell'), this._screen);
commandParams.shell = commandModule.shell;
if (this.needsDocker(commandModule)) {
commandModule.docker = new Docker(
this._logger.sublogger('Docker'),
this._screen,
commandModule.shell,
this._environment);
commandParams.docker = commandModule.docker;
}
if (commandModule.useLocalDeployer)
{
commandParams.localDeployer = new LocalDeployer(
this._logger.sublogger('LocalDeployer'),
this._configRegistry,
commandModule.docker,
this._screen,
commandModule.shell,
this._environment,
this._storage);
}
if (commandModule.usePlantUml)
{
commandParams.PlantUml = PlantUml;
}
if (commandModule.useDevTools)
{
const DevTools = require('./tools/dev-tools');
commandParams.devTools = new DevTools(this._logger, this._rootDir);
}
if (commandModule.useClient) {
commandParams.client = this._client;
commandParams.dataProvider = new AccountDataProvider(this._client);
if (commandParams.args["skip-ssl"]) {
this._screen.info("NOTE: Skipped SSL checks.")
commandParams.client.setSkipSSL(true);
} else {
commandParams.client.setSkipSSL(false);
}
}
if (commandModule.useGcpHelper) {
commandParams.gcpHelper = new GcpHelper(
commandParams.logger,
commandParams.screen,
commandParams.dataProvider,
commandParams.inputError,
commandParams.shell,
commandParams.storage
);
}
if (commandModule.usePrompt)
{
var prompt = require('prompt');
commandModule.prompt = prompt;
commandParams.runPrompt = (schema) =>
{
return new Promise(function(resolve, reject) {
prompt.start();
prompt.message = null;
prompt.get(schema, (err, result) => {
if (err)
{
reject(err);
}
else
{
resolve(result);
}
});
});
}
}
if (commandModule.useWaiter)
{
var isEnabled = true;
if (!commandModule.skipWaitFlag) {
isEnabled = commandParams.args["wait"];
}
commandParams.waiter = new Waiter(this._logger.sublogger("Waiter"),
this._screen,
this._client,
isEnabled,
commandParams.args["timeout"]);
}
return Promise.resolve()
.then(() => this.readyToRun(commandModule, commandParams))
.then(() => {
// return commandModule.docker.isDockerRunning()
// .then(res => {
// this._logger.info('Docker Info: ', res);
// });
})
.then(() => {
if (commandModule.useProject)
{
return this._loadDefinitions(commandParams.args["pathoverride"], commandParams.args["ignoredirs"])
.then(registry => {
commandParams.registry = registry
var validator = registry.validate();
validator.enforce();
});
}
})
.then(() => {
this._logger.verbose('Launching command %s...', info.command);
})
.then(() => {
if (commandModule.fetch) {
return Promise.resolve(commandModule.fetch(commandParams))
.then(result => {
commandParams.result = result;
commandResult = result;
});
}
})
.then(() => {
if (!args.options['fetch-only']) {
return Promise.resolve(commandModule.exec(commandParams))
.then(result => {
commandResult = result;
})
}
})
.then(() => {
this._logger.verbose('Saving config data...');
})
.then(() => this._configRegistry.saveData())
.then(() => {
this._logger.verbose('Config data saved.');
})
.catch(error => {
this._handleCommandError(info.command, error, commandModule);
})
.then(() => {
this._commandCleanup(commandModule);
})
.then(() => {
return commandResult;
});
} catch(error) {
this._handleCommandError(info.command, error, commandModule);
}
}
_commandCleanup(commandModule)
{
if (commandModule.usePrompt)
{
if (commandModule.prompt)
{
commandModule.prompt.stop();
commandModule.prompt = null;
}
}
}
readyToRun(commandModule, commandParams)
{
return Promise.resolve()
.then(() => this._checkDocker(commandModule, commandParams))
;
}
_checkDocker(commandModule, commandParams)
{
if (this.needsDocker(commandModule)) {
this._logger.info('Checking docker...');
return commandModule.docker.isDockerRunning()
.then(result => {
if (!result) {
throw new Errors.MissingPrerequisite('Docker not running.');
}
})
}
}
needsDocker(commandModule)
{
if (commandModule.useDocker || commandModule.useLocalDeployer) {
return true;
}
return false;
}
_handleCommandError(commandName, error, commandModule)
{
if (commandModule)
{
this._commandCleanup(commandModule);
}
if (error instanceof CommonErrors.Entity) {
this._screen.info(error.message);
error.validator.outputScreen(this._screen);
return this.terminate(5);
} else if (error instanceof Errors.Auth) {
if (!error.message) {
this._screen.error('Unauthorized.');
} else {
this._screen.error(error.message)
}
this._screen.info('Please login using: \'berlioz login\', or register using: \'berlioz signup\'.')
return this.terminate(1);
} else if (error instanceof Errors.MissingPrerequisite) {
this._screen.error(error.message);
return this.terminate(2);
} else if (error instanceof Errors.Input) {
this._screen.error(error.message);
return this.terminate(3);
} else if (error instanceof CommonErrors.Generic) {
this._screen.error(error.message);
return this.terminate(4);
}
// this._logger.exception(error);
this._logger.warn('Error running command \"%s\"', commandName);
var commandStr = commandName.join(' ');
this._screen.error('Error running command %s.', commandStr);
this._logger.warn('Reason: %s.', error.message);
this._screen.error('Reason: %s', error.message);
return this.terminate(5);
}
_autocompleteProcessor(name)
{
return this._autocomplete.create(name);
}
_loadDefinitions(pathOverride, ignoredirs)
{
this._logger.info("[_loadDefinitions] pathOverride=%s, ignoredirs=%s", pathOverride, ignoredirs)
var registry = null;
var dirs = null;
if (pathOverride) {
if (_.isString(pathOverride)) {
dirs = pathOverride.split(',')
}
} else {
dirs = ['.']
}
if (ignoredirs) {
if (_.isString(ignoredirs)) {
ignoredirs = ignoredirs.split(',');
}
} else {
ignoredirs = [];
}
return Promise.resolve()
.then(() => {
var loader = new Loader(this._logger)
return loader.fromDirs(this._rootDir, dirs, ignoredirs)
})
.then(result => {
this._logger.verbose('Definitions Loaded.');
registry = result;
// this._logger.debug('Registry loaded', registry);
return registry;
})
}
parseDefinitions(definitions)
{
var loader = new Loader(this._logger)
var registry = null;
return Promise.resolve()
.then(() => loader.fromDefinitions(definitions))
.then(result => {
this._logger.info('Definitions Loaded.');
registry = result;
this._logger.silly('Registry loaded', registry);
return registry;
})
}
terminate(code)
{
this._logger.flush();
process.exitCode = code;
// process.exit(code);
}
}
module.exports = Starter;