Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Serverless Run Plugin #4034

Merged
merged 77 commits into from
Aug 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
52f743b
first draft of serverless run
eahefnawy Jul 24, 2017
997d458
connect with local emulator
eahefnawy Jul 26, 2017
6443bf3
local emulator updates
eahefnawy Jul 28, 2017
a63ee7a
serverless run: refactoring
eahefnawy Aug 2, 2017
da91c5a
installing latest eventgateway
eahefnawy Aug 3, 2017
564abd8
updated FDK
eahefnawy Aug 3, 2017
670b955
updated http events
eahefnawy Aug 7, 2017
a811534
added temp dirs for event gateway
eahefnawy Aug 8, 2017
9e45094
Merge branch 'master' into serverless-run
nikgraf Aug 8, 2017
bdde712
fix linting error
nikgraf Aug 8, 2017
1c0da07
upgrade fdk to the latest version
nikgraf Aug 8, 2017
213fb74
Merge branch 'master' into serverless-run
nikgraf Aug 9, 2017
41c01b6
introduce logEventGateway to prepare for upcoming log format
nikgraf Aug 9, 2017
e84bc39
install local emulator if functions not deployed
eahefnawy Aug 9, 2017
824a2dc
update eventgateway and local emulator
eahefnawy Aug 10, 2017
8730645
improve logging
nikgraf Aug 10, 2017
a89a568
improve log experience
nikgraf Aug 10, 2017
910255d
improve colors for the sls run output
nikgraf Aug 10, 2017
a0a1dd3
fix newline in run and fix test
nikgraf Aug 10, 2017
d8cdc49
fix linting
nikgraf Aug 10, 2017
9bd7ef4
add try/catch to avoid critical failure
nikgraf Aug 10, 2017
65ce09b
fix log output for latest gateway
nikgraf Aug 11, 2017
3e8d847
improve log output of emit
nikgraf Aug 11, 2017
95e1abe
added gcf
eahefnawy Aug 10, 2017
29585bb
major refactoring
eahefnawy Aug 11, 2017
a59536b
more refactoring
eahefnawy Aug 11, 2017
01718e8
prettify more event gateway logs and fix multiple logs coming in at t…
nikgraf Aug 11, 2017
deeb82f
improve boot logs for event gateway
nikgraf Aug 11, 2017
d338332
add unsubscribe
nikgraf Aug 11, 2017
841b271
fix test
nikgraf Aug 11, 2017
76fef37
added response to logs
eahefnawy Aug 11, 2017
eccbca3
switch to data
nikgraf Aug 11, 2017
a798496
Add newline at end of Function invoked log
alexdebrie Aug 12, 2017
e637c65
Add debug flag to serverless run command
brianneisler Aug 12, 2017
04c144f
Update to fit new emulator API
brianneisler Aug 13, 2017
2506b79
fix linting issues
eahefnawy Aug 13, 2017
59656e1
modify output
austencollins Aug 12, 2017
48d6b4b
add modifications
austencollins Aug 12, 2017
7ff2e51
minor mods
austencollins Aug 12, 2017
04ba2fc
improve invocation logging
austencollins Aug 12, 2017
10b8c96
Merge pull request #4081 from serverless/serverless-run-mods
austencollins Aug 13, 2017
da85ab1
remove elipsis and log statement
austencollins Aug 13, 2017
bb58619
fix formatting of function invoked log statement
austencollins Aug 13, 2017
fd5e70c
fix emit style and clean up linting errors
nikgraf Aug 14, 2017
0f64ff0
fix tests
nikgraf Aug 14, 2017
22b151f
bring back fallback logging
nikgraf Aug 14, 2017
b1dc0c4
implement function finished and triggered
nikgraf Aug 14, 2017
edfdf77
improve emit and fix tests
nikgraf Aug 14, 2017
9e55758
Remove side-effects / fix bug when deploying large functions
pmuens Aug 14, 2017
56086dd
Add code comment about deployments of large functions
pmuens Aug 14, 2017
f809d30
improve logs when deploying a second service
nikgraf Aug 14, 2017
8a5113d
fix testsuite
nikgraf Aug 14, 2017
30451fc
another try to fix the testsuite
nikgraf Aug 14, 2017
ff1ff85
Add project to functionConfig for Google provider-functions
pmuens Aug 14, 2017
08bec7b
Add additional Google Cloud Functions related config
pmuens Aug 14, 2017
7da35d6
introduce function not found
nikgraf Aug 14, 2017
2bcd836
fix test-suite by disabling color matching
nikgraf Aug 14, 2017
b7aa82c
simplify trigger function
nikgraf Aug 14, 2017
cfd6b21
minor tweaks to formatting
austencollins Aug 14, 2017
09a8d4f
minor tweak to output styling
austencollins Aug 14, 2017
91d746d
Add separation between platform and framework commands
brianneisler Aug 15, 2017
fca3015
serverless run ui improvements
austencollins Aug 15, 2017
35499a5
serverless run ui modifications
austencollins Aug 15, 2017
1720e2d
minor indentation tweak
austencollins Aug 15, 2017
e176dc8
run without functions and lock versions
eahefnawy Aug 15, 2017
909fae7
Merge branch 'master' into serverless-run
nikgraf Aug 15, 2017
2c722c2
fix tests
nikgraf Aug 15, 2017
c7e972c
Merge branch 'master' into serverless-run
nikgraf Aug 15, 2017
082f1bd
updated lockfile
nikgraf Aug 15, 2017
8fc34f6
Fix Node.js 4.x compatibility
pmuens Aug 15, 2017
699b9bc
color emulator errors
nikgraf Aug 15, 2017
73953d0
Update to use new Emulator and Event Gateway versions
pmuens Aug 15, 2017
111dea3
Fix noFunctions detection bug
pmuens Aug 15, 2017
23cff7f
Add detection of processor arch for Event Gateway binary downloads
pmuens Aug 15, 2017
c0d53b2
add event gateway config info to logs
austencollins Aug 15, 2017
94318ae
remove unnecessary console log
austencollins Aug 15, 2017
b1ae5a5
upgrade to latest event-gateway
nikgraf Aug 15, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 38 additions & 8 deletions lib/classes/CLI.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,54 @@ class CLI {
}

generateMainHelp() {
let platformCommands;
let frameworkCommands;
if (this.loadedCommands) {
const commandKeys = Object.keys(this.loadedCommands);
const sortedCommandKeys = _.sortBy(commandKeys);
const partitionedCommandKeys = _.partition(sortedCommandKeys,
(key) => this.loadedCommands[key].platform);
platformCommands = _.fromPairs(
_.map(partitionedCommandKeys[0], key => [key, this.loadedCommands[key]])
);
frameworkCommands = _.fromPairs(
_.map(partitionedCommandKeys[1], key => [key, this.loadedCommands[key]])
);
}

this.consoleLog('');

this.consoleLog(chalk.yellow.underline('Commands'));
this.consoleLog(chalk.dim('* Serverless documentation: http://docs.serverless.com'));
this.consoleLog(chalk.dim('* You can run commands with "serverless" or the shortcut "sls"'));
this.consoleLog(chalk.dim('* Pass "--verbose" to this command to get in-depth plugin info'));
this.consoleLog(chalk.dim('* Pass "--help" after any <command> for contextual help'));

this.consoleLog('');

if (this.loadedCommands) {
const commandKeys = Object.keys(this.loadedCommands);
const sortedCommandKeys = _.sortBy(commandKeys);
const sortedCommands = _.fromPairs(
_.map(sortedCommandKeys, key => [key, this.loadedCommands[key]])
);
this.consoleLog(chalk.yellow.underline('Framework'));
this.consoleLog(chalk.dim('* Documentation: https://serverless.com/framework/docs/'));

this.consoleLog('');

if (!_.isEmpty(frameworkCommands)) {
_.forEach(frameworkCommands, (details, command) => {
this.displayCommandUsage(details, command);
});
} else {
this.consoleLog('No commands found');
}

this.consoleLog('');

this.consoleLog(chalk.yellow.underline('Platform (Beta)'));
// eslint-disable-next-line max-len
this.consoleLog(chalk.dim('* The Serverless Platform is currently in experimental beta. Follow the docs below to get started.'));
this.consoleLog(chalk.dim('* Documentation: https://serverless.com/platform/docs/'));

this.consoleLog('');

_.forEach(sortedCommands, (details, command) => {
if (!_.isEmpty(platformCommands)) {
_.forEach(platformCommands, (details, command) => {
this.displayCommandUsage(details, command);
});
} else {
Expand Down
1 change: 1 addition & 0 deletions lib/plugins/Plugins.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"plugins": [
"./run/index.js",
"./config/config.js",
"./create/create.js",
"./install/install.js",
Expand Down
29 changes: 23 additions & 6 deletions lib/plugins/emit/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
'use strict';

const os = require('os');
const BbPromise = require('bluebird');
const fdk = require('@serverless/fdk');
const path = require('path');
const stdin = require('get-stdin');
const userStats = require('../../utils/userStats');
const chalk = require('chalk');

const spaceSmall = ' ';
const spaceLarge = ' ';
const colorDim = chalk.hex('#777777');
const colorPrefix = chalk.hex('#bdb018');
const prefix = colorPrefix(` Serverless ${spaceSmall}`);

const prettifyValue = value => {
const prettified = JSON.stringify(value, null, 2).replace(
new RegExp('\\n', 'g'),
`\n${spaceLarge}`
);
return `${spaceLarge}${colorDim(prettified)}`;
};

class Emit {
constructor(serverless, options) {
Expand Down Expand Up @@ -39,6 +55,7 @@ class Emit {
shortcut: 't',
},
},
platform: true,
},
};

Expand Down Expand Up @@ -117,14 +134,14 @@ class Emit {
return eventGateway
.emit(emitParams)
.then(() => {
const msg = `Successfully emitted the event ${name} as datatype ${emitParams.dataType ||
'application/json'} with:`;
this.serverless.cli.consoleLog(`${msg}\n${JSON.stringify(data)}`);
const msg = `${prefix}Emitted the event ${name} as datatype ${emitParams.dataType ||
'application/json'}:`;
this.serverless.cli.consoleLog(`${msg}${os.EOL}${prettifyValue(data)}`);
})
.catch(() => {
const msg = `Failed to emit the event ${name} as datatype ${emitParams.dataType ||
'application/json'} with:`;
this.serverless.cli.consoleLog(`${msg}\n${JSON.stringify(data)}`);
const msg = `${prefix}Failed to emit the event ${name} as datatype ${emitParams.dataType ||
'application/json'}:`;
this.serverless.cli.consoleLog(`${msg}${os.EOL}${prettifyValue(data)}`);
throw new Error(`Failed to emit the event ${name}`);
});
}
Expand Down
18 changes: 8 additions & 10 deletions lib/plugins/emit/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,10 @@ describe('Emit', () => {
data: emit.data,
})
).to.equal(true);
expect(logStub.calledOnce).to.equal(true);
const msgPartOne = 'Successfully emitted the event userCreated as datatype';
expect(
logStub.calledWithExactly(`${msgPartOne} application/json with:\n{"key":"value"}`)
).to.equal(true);
expect(logStub.getCall(0).args[0]).to.equal(
// eslint-disable-next-line max-len
' Serverless Emitted the event userCreated as datatype application/json:\n {\n "key": "value"\n }'
);
});
});

Expand All @@ -162,11 +161,10 @@ describe('Emit', () => {
dataType: 'text/plain',
})
).to.equal(true);
expect(logStub.calledOnce).to.equal(true);
const msgPartOne = 'Successfully emitted the event userCreated as datatype';
expect(
logStub.calledWithExactly(`${msgPartOne} text/plain with:\n"This is a message"`)
).to.equal(true);
expect(logStub.getCall(0).args[0]).to.equal(
// eslint-disable-next-line max-len
' Serverless Emitted the event userCreated as datatype text/plain:\n "This is a message"'
);
});
});
});
Expand Down
1 change: 1 addition & 0 deletions lib/plugins/login/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Login {
lifecycleEvents: [
'login',
],
platform: true,
},
};

Expand Down
5 changes: 3 additions & 2 deletions lib/plugins/logout/logout.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const userStats = require('../../utils/userStats');
const configUtils = require('../../utils/config');

class Login {
class Logout {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
Expand All @@ -12,6 +12,7 @@ class Login {
logout: {
usage: 'Logout from the Serverless Platform',
lifecycleEvents: ['logout'],
platform: true,
},
};

Expand Down Expand Up @@ -50,4 +51,4 @@ class Login {
}
}

module.exports = Login;
module.exports = Logout;
148 changes: 148 additions & 0 deletions lib/plugins/run/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
'use strict';

const BbPromise = require('bluebird');

const logServerless = require('./utils/logServerless');
const getLocalRootUrl = require('./utils/getLocalRootUrl');

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 deployFunctionsToLocalEmulator = require('./utils/deployFunctionsToLocalEmulator');
const registerFunctionsToEventGateway = require('./utils/registerFunctionsToEventGateway');

const manageLocalEmulator = require('./utils/manageLocalEmulator');
const manageEventGateway = require('./utils/manageEventGateway');

class Run {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.commands = {
run: {
usage: 'Runs the Event Gateway and the Emulator',
lifecycleEvents: [
'run',
],
options: {
eport: {
usage: 'The Event Gateway API port',
shortcut: 'e',
default: '4000',
},
cport: {
usage: 'The Event Gateway configuration port',
shortcut: 'c',
default: '4001',
},
lport: {
usage: 'The Emulator port',
shortcut: 'l',
default: '4002',
},
debug: {
usage: 'Start the debugger',
shortcut: 'd',
},
},
platform: true,
},
};

this.hooks = {
'run:run': () => BbPromise.bind(this)
.then(this.run),
};
}

run() {
const EVENT_GATEWAY_VERSION = '0.5.14';
const LOCAL_EMULATOR_VERSION = '0.1.18';

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) {
// logServerless('Emulator already running');
functionsDeployed = true;
return deployFunctionsToLocalEmulator(
this.serverless.service,
this.serverless.config.servicePath,
getLocalRootUrl(this.options.lport)
).then(noFunctions => {
if (noFunctions) {
// eslint-disable-next-line max-len
logServerless('Service does not contain any functions to be loaded in your current "serverless run" session.');
return BbPromise.resolve();
}
// eslint-disable-next-line max-len
logServerless('Functions loaded successfully in your current "serverless run" session.');
return BbPromise.resolve();
});
}
return BbPromise.resolve();
})
.then(() => eventGatewayRunning(getLocalRootUrl(this.options.cport)))
.then(eventGatewayAlreadyRunning => {
if (eventGatewayAlreadyRunning) {
functionsRegistered = true;
// logServerless('Event Gateway already running');
return registerFunctionsToEventGateway(
this.serverless.service,
getLocalRootUrl(this.options.eport),
getLocalRootUrl(this.options.cport),
getLocalRootUrl(this.options.lport)
).then(() => {
// eslint-disable-next-line max-len
// logServerless('Functions and subscriptions registered. For details please review the terminal running the Event Gateway.');
});
}
return BbPromise.resolve();
})
.then(() => {
if (!functionsDeployed && !localEmulatorInstalled(LOCAL_EMULATOR_VERSION)) {
logServerless('Installing Emulator');
installLocalEmulator(LOCAL_EMULATOR_VERSION);
}
return BbPromise.resolve();
})
.then(() => {
if (!eventGatewayInstalled(EVENT_GATEWAY_VERSION)) {
logServerless('Installing Event Gateway');
return installEventGateway(EVENT_GATEWAY_VERSION);
}
return BbPromise.resolve();
})
.then(() => {
if (!functionsDeployed) {
return manageLocalEmulator(this.serverless.service,
this.serverless.config.servicePath,
{ port: this.options.lport, debug: this.options.debug });
}
return BbPromise.resolve();
})
.then(() => {
if (!functionsRegistered) {
return manageEventGateway(this.serverless.service,
this.options.eport,
this.options.cport,
this.options.lport);
}
return BbPromise.resolve();
});
}
}

module.exports = Run;
21 changes: 21 additions & 0 deletions lib/plugins/run/utils/deployFunctionToLocalEmulator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const fetch = require('node-fetch');

function deployFunctionToLocalEmulator(functionId, functionConfig, emulatorUrl) {
const localEmulatorDeployEndpoint = `${emulatorUrl}/v0/emulator/api/functions/deploy`;

return fetch(localEmulatorDeployEndpoint, {
headers: {
'content-type': 'application/json',
},
method: 'POST',
timeout: 0, // NOTE using 0 so that deployments of large functions won't timeout
body: JSON.stringify({
functionId,
functionConfig,
}),
});
}

module.exports = deployFunctionToLocalEmulator;
37 changes: 37 additions & 0 deletions lib/plugins/run/utils/deployFunctionsToLocalEmulator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const _ = require('lodash');
const BbPromise = require('bluebird');
const getLocalEmulatorFunctionConfig = require('./getLocalEmulatorFunctionConfig');
const deployFunctionToLocalEmulator = require('./deployFunctionToLocalEmulator');
const logLocalEmulator = require('./logLocalEmulator');

function deployFunctionsToLocalEmulator(service, servicePath, localEmulatorRootUrl) {
const functionDeploymentPromises = [];
let noFunctions = false;

_.each(service.functions, (functionConfig, functionName) => {
const localEmulatorFunctionConfig = getLocalEmulatorFunctionConfig(
functionConfig,
service.provider,
servicePath);

const deployFunctionToLocalEmulatorPromise = deployFunctionToLocalEmulator(
`${service.service}-${functionName}`,
localEmulatorFunctionConfig, localEmulatorRootUrl);
functionDeploymentPromises.push(deployFunctionToLocalEmulatorPromise);
});

if (functionDeploymentPromises.length === 0) {
noFunctions = true;
return BbPromise.resolve(noFunctions);
}

// NOTE important for UX since it takes a while to upload large functions
logLocalEmulator('Functions loading...');

return BbPromise.all(functionDeploymentPromises)
.then(() => noFunctions);
}

module.exports = deployFunctionsToLocalEmulator;