2017-08-03 20:40:24 +07:00

226 lines
7.4 KiB
JavaScript

'use strict';
const BbPromise = require('bluebird');
const _ = require('lodash');
const chalk = require('chalk');
const childProcess = BbPromise.promisifyAll(require('child_process'));
const fdk = require('@serverless/fdk');
const path = require('path');
const os = require('os');
const getLocalRootUrl = require('./utils/getLocalRootUrl');
const getLocalEmulatorFunctionConfig = require('./utils/getLocalEmulatorFunctionConfig');
const deployFunctionToLocalEmulator = require('./utils/deployFunctionToLocalEmulator');
const localEmulatorRunning = require('./utils/localEmulatorRunning');
const eventGatewayRunning = require('./utils/eventGatewayRunning');
const localEmulatorInstalled = require('./utils/localEmulatorInstalled');
const eventGatewayInstalled = require('./utils/eventGatewayInstalled');
const installLocalEmulator = require('./utils/installLocalEmulator');
const installEventGateway = require('./utils/installEventGateway');
const getLatestEventGatewayVersion = require('./utils/getLatestEventGatewayVersion');
class Run {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.commands = {
run: {
usage: 'Runs the Event Gateway and the Local Emulator',
lifecycleEvents: [
'run',
],
options: {
eport: {
usage: 'The Event Gateway API port',
shortcut: 'e',
default: '4000',
},
cport: {
usage: 'The Event Gateway configuration port',
shortcut: 'e',
default: '4001',
},
lport: {
usage: 'The Local Emulator port',
shortcut: 'l',
default: '4002',
},
},
},
};
this.hooks = {
'run:run': () => BbPromise.bind(this)
.then(this.run),
};
}
run() {
let functionsDeployed = false;
let functionsRegistered = false;
if (!this.serverless.config.servicePath) {
throw new this.serverless.classes
.Error('This command can only run inside a service');
}
return localEmulatorRunning(getLocalRootUrl(this.options.lport))
.then(localEmulatorAlreadyRunning => {
if (localEmulatorAlreadyRunning) {
this.logServerless('Local Emulator Already Running');
functionsDeployed = true;
return this.deployFunctionsToLocalEmulator();
}
return BbPromise.resolve();
})
.then(() => eventGatewayRunning(getLocalRootUrl(this.options.cport)))
.then(eventGatewayAlreadyRunning => {
if (eventGatewayAlreadyRunning) {
functionsRegistered = true;
this.logServerless('Event Gateway Already Running');
return this.registerFunctionsToEventGateway();
}
return BbPromise.resolve();
})
.then(() => {
if (!localEmulatorInstalled()) {
this.logServerless('Installing Local Emulator...');
installLocalEmulator();
}
return BbPromise.resolve();
})
.then(() => {
if (!eventGatewayInstalled()) {
return getLatestEventGatewayVersion()
.then(version => installEventGateway(version));
}
return BbPromise.resolve();
})
.then(() => {
return new BbPromise((resolve, reject) => {
if (!functionsDeployed) {
let initialized = false;
this.logServerless('Spinning Up the Local Emulator...');
const localEmulatorChildProcess = childProcess.spawn('sle',
['--port', this.options.lport]);
localEmulatorChildProcess.stdout.on('data', chunk => {
this.logLocalEmulator(chunk.toString('utf8'));
if (!initialized) {
initialized = true;
return this.deployFunctionsToLocalEmulator();
}
return BbPromise.resolve();
});
localEmulatorChildProcess.stderr.on('data', chunk => {
this.logLocalEmulator(chunk.toString('utf8'));
});
localEmulatorChildProcess.on('close', () => resolve());
localEmulatorChildProcess.on('error', error => reject(error));
}
if (!functionsRegistered) {
let initialized = false;
const eventGatewayBinaryFilePath = path
.join(os.homedir(), '.serverless', 'event-gateway', 'event-gateway');
this.logServerless('Spinning Up the Event Gateway...');
const eventGatewayChildProcess = childProcess
.spawn(eventGatewayBinaryFilePath, ['--dev']);
eventGatewayChildProcess.stdout.on('data', chunk => {
this.logEventGateway(chunk.toString('utf8'));
});
eventGatewayChildProcess.stderr.on('data', chunk => {
this.logEventGateway(chunk.toString('utf8'));
if (!initialized) {
initialized = true;
return this.registerFunctionsToEventGateway();
}
});
eventGatewayChildProcess.on('close', () => resolve());
eventGatewayChildProcess.on('error', error => reject(error));
}
});
});
}
deployFunctionsToLocalEmulator() {
this.logServerless('Deploying Functions to Local Emulator...');
const service = this.serverless.service;
const serviceName = service.service;
const providerConfig = service.provider;
const functionDeploymentPromises = [];
_.each(service.functions, (functionConfig,
functionName) => {
const localEmulatorFunctionConfig = getLocalEmulatorFunctionConfig(
functionConfig,
providerConfig,
this.serverless.config.servicePath);
const deployFunctionToLocalEmulatorPromise = deployFunctionToLocalEmulator(
serviceName, functionName,
localEmulatorFunctionConfig, getLocalRootUrl(this.options.lport));
functionDeploymentPromises.push(deployFunctionToLocalEmulatorPromise);
});
return BbPromise.all(functionDeploymentPromises)
.then(() => this.logServerless('Functions Deployed!'));
}
registerFunctionsToEventGateway() {
this.logServerless('Registering Functions to the Event Gateway...');
const gateway = fdk.eventGateway({
url: getLocalRootUrl(this.options.eport),
configurationUrl: getLocalRootUrl(this.options.cport),
});
const functionsArray = [];
const service = this.serverless.service;
const serviceName = service.service;
_.each(service.functions, (functionConfig,
functionName) => {
const functionId = `${serviceName}${functionName}`;
const invokeFunctionUrl = `${getLocalRootUrl(this.options.lport)
}/v0/emulator/api/invoke/${serviceName}/${functionName}`;
const functionObject = {
functionId,
provider: {
type: 'http',
url: invokeFunctionUrl,
},
};
functionsArray.push(functionObject);
});
setTimeout(() => {
return gateway.configure({ functions: functionsArray })
.then(() => this.logServerless('Functions Registered in the Event Gateway!'));
}, 3000);
}
logServerless(message) {
process.stdout.write(chalk.yellow(` Serverless | ${message}\n`));
}
logLocalEmulator(message) {
process.stdout.write(chalk.green(` Local Emulator | ${message.trim()}\n`));
}
logEventGateway(message) {
process.stdout.write(chalk.blue(` Event Gateway | ${message.trim()}\n`));
}
}
module.exports = Run;