Merge remote-tracking branch 'upstream/v0.5' into v0.5-provide-reserved-variable-name-to-endpoints

This commit is contained in:
Erik Erikson 2016-03-18 12:19:08 -07:00
commit a38621ba8a
14 changed files with 191 additions and 216 deletions

View File

@ -11,7 +11,7 @@ module.exports = function(S) {
* This is the base class that all Serverless Plugins should extend.
*/
class ServerlessPlugin {
class Plugin {
/**
* Constructor
@ -28,6 +28,9 @@ module.exports = function(S) {
static getName() {
return this.name;
}
getName() {
return this.constructor.getName();
}
/**
* Register Actions
@ -235,7 +238,7 @@ module.exports = function(S) {
}
return ServerlessPlugin;
return Plugin;
};

View File

@ -59,17 +59,15 @@ module.exports = function(S) {
let req = awsService[method](params);
// TODO: Add listeners, put Debug statments here...
//req.on('validate', function (r) {});
// req.on('send', function (r) {console.log(r)});
return S.utils.persistentRequest(function () {
return new BbPromise(function (resolve, reject) {
req.send(function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
return new BbPromise(function (res, rej) {
req.send(function (err, data) {
if (err) {
rej(err);
} else {
res(data);
}
});
});
}
@ -342,8 +340,8 @@ module.exports = function(S) {
return vars.accountId;
} else {
return vars.iamRoleArnLambda
.replace('arn:aws:iam::', '')
.split(':')[0];
.replace('arn:aws:iam::', '')
.split(':')[0];
}
}
}

View File

@ -16,7 +16,7 @@ module.exports = function(S) {
if (data) this.fromObject(data);
this.variables = new S.classes.Variables(S);
this.variables = new S.classes.Variables();
}
load() {
@ -33,7 +33,7 @@ module.exports = function(S) {
fromObject(data) {
if (data.variables) {
this.variables = new S.classes.Variables(S, data.variables);
this.variables = new S.classes.Variables(data.variables);
delete data.variables;
}
return _.assign(this, data);
@ -76,5 +76,4 @@ module.exports = function(S) {
}
return Region;
};

View File

@ -288,6 +288,7 @@ class Serverless {
let pluginPath = path.join(relDir, 'plugins', pluginMetadatum);
_this.utils.sDebug('Attempting to load plugin from ' + pluginPath);
PluginClass = loadPlugin(pluginPath);
PluginClass = PluginClass(_this);
} else {
throw new SError(`This plugin could not be found: ${pluginMetadatum}`);
}

View File

@ -64,11 +64,10 @@ module.exports = function(S) {
* Return EVT
*/
_this.evt.data.functioName = _this.functionName;
_this.evt.data.pathCompressed = _this.pathCompressed;
_this.evt.data.lambdaVersion = _this.lambdaVersion;
_this.evt.data.lambdaAlias = _this.lambdaAlias;
_this.evt.data.lambdaAliasArn = _this.lambdaAliasArn;
_this.evt.data.functioName = _this.functionName;
_this.evt.data.pathCompressed = _this.pathCompressed;
_this.evt.data.functionVersion = _this.functionVersion;
_this.evt.data.functionAliasArn = _this.functionAliasArn;
return _this.evt;
});
@ -185,7 +184,7 @@ module.exports = function(S) {
.then(function (data) {
// Save Version & Lambda
_this.lambdaVersion = data.Version;
_this.functionVersion = data.Version;
_this.lambda = data;
})
@ -223,7 +222,7 @@ module.exports = function(S) {
.then(function (data) {
// Save Version & Lambda
_this.lambdaVersion = data.Version;
_this.functionVersion = data.Version;
_this.lambda = data;
});
});
@ -237,13 +236,13 @@ module.exports = function(S) {
_alias() {
let _this = this;
let aliasedLambda = false;
_this.lambdaAlias = _this.evt.options.stage.toLowerCase();
let _this = this;
let aliasedLambda = false;
_this.functionAlias = _this.evt.options.functionAlias ? _this.evt.options.functionAlias : _this.evt.options.stage.toLowerCase();
var params = {
FunctionName: _this.lambda.FunctionName, /* required */
Name: _this.lambdaAlias /* required */
Name: _this.functionAlias /* required */
};
return _this.aws.request('Lambda', 'getAlias', params, _this.evt.options.stage, _this.evt.options.region)
@ -258,12 +257,12 @@ module.exports = function(S) {
// Update Existing Alias
SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Updating Lambda Alias for version - ${_this.lambdaVersion}`);
SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Updating Lambda Alias for version - ${_this.functionVersion}`);
let params = {
FunctionName: _this.lambda.FunctionName, /* required */
FunctionVersion: _this.lambdaVersion, /* required */
Name: _this.lambdaAlias, /* required */
FunctionVersion: _this.functionVersion, /* required */
Name: _this.functionAlias, /* required */
Description: 'Project: '
+ _this.project.name
+ ' Stage: '
@ -276,12 +275,12 @@ module.exports = function(S) {
// Create New Alias
SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Creating New Lambda Alias for version - ${_this.lambdaVersion}`);
SUtils.sDebug(`"${_this.evt.options.stage} - ${_this.evt.options.region} - ${_this.functionName}": Creating New Lambda Alias for version - ${_this.functionVersion}`);
let params = {
FunctionName: _this.lambda.FunctionName, /* required */
FunctionVersion: _this.lambdaVersion, /* required */
Name: _this.lambdaAlias, /* required */
FunctionVersion: _this.functionVersion, /* required */
Name: _this.functionAlias, /* required */
Description: 'Project: '
+ _this.project.name
+ ' Stage: '
@ -294,7 +293,7 @@ module.exports = function(S) {
.then(function(data) {
// Save Alias
_this.lambdaAliasArn = data.AliasArn;
_this.functionAliasArn = data.AliasArn;
});
}
}

View File

@ -817,7 +817,7 @@ module.exports = function(S) {
+ _this.endpoint.path;
return _this.aws.request('Lambda', 'addPermission', params, _this.evt.options.stage, _this.evt.options.region)
.then(function() {
.then(function(data) {
SUtils.sDebug(
'"'

View File

@ -292,7 +292,7 @@ module.exports = function(S) {
} else {
return _this._updateAuthorizer(f, fPopulated, updateId, region);
}
});
})
}
/**
@ -311,8 +311,7 @@ module.exports = function(S) {
authorizer.restApiId = _this.restApiData.id;
authorizer.name = f.name;
authorizer.type = authorizer.type || 'TOKEN';
let alias = '${stageVariables.functionAlias}';
authorizer.authorizerResultTtlInSeconds = authorizer.authorizerResultTtlInSeconds || "300";
// Construct authorizer URI
authorizer.authorizerUri = 'arn:aws:apigateway:'
@ -324,7 +323,7 @@ module.exports = function(S) {
+ ':function:'
+ f.getDeployedName({ stage: _this.evt.options.stage, region})
+ ':'
+ alias
+ '${stageVariables.functionAlias}'
+ '/invocations';
return _this.provider.request('APIGateway', 'createAuthorizer', authorizer, _this.evt.options.stage, region)
@ -335,7 +334,7 @@ module.exports = function(S) {
// Add permission to the authorizer Lambda so API G can access it
let functionName = f.getDeployedName({ stage: _this.evt.options.stage, region});
return _this._addLambdaPermissionForAuthorizer(functionName, region);
return _this._addLambdaPermissionForAuthorizer(_this.authorizers[f.name], functionName, region);
});
}
@ -355,9 +354,9 @@ module.exports = function(S) {
let authorizer = fPopulated.authorizer;
params.restApiId = _this.restApiData.id;
params.authorizerId = updateId;
authorizer.authorizerResultTtlInSeconds = authorizer.authorizerResultTtlInSeconds || "300";
// Construct authorizer URI
let alias = '${stageVariables.functionAlias}';
let authorizerUri = 'arn:aws:apigateway:'
+ region
+ ':lambda:path/2015-03-31/functions/arn:aws:lambda:'
@ -367,7 +366,7 @@ module.exports = function(S) {
+ ':function:'
+ f.getDeployedName({ stage: _this.evt.options.stage, region })
+ ':'
+ alias
+ '${stageVariables.functionAlias}'
+ '/invocations';
// Update authorizer properties
@ -402,13 +401,14 @@ module.exports = function(S) {
// Perform update
return _this.provider.request('APIGateway', 'updateAuthorizer', params, _this.evt.options.stage, region)
.then(function(a) {
_this.authorizers[f.name] = a.id;
// Add permission to the authorizer Lambda so API G can access it
let functionName = f.getDeployedName({ stage: _this.evt.options.stage, region });
return _this._removeLambdaPermissionForAuthorizer(functionName, region)
.then(function() {
return _this._addLambdaPermissionForAuthorizer(functionName, region);
return _this._addLambdaPermissionForAuthorizer(_this.authorizers[f.name], functionName, region);
});
});
}
@ -479,7 +479,7 @@ module.exports = function(S) {
* Add Lambda Permission For Authorizer
*/
_addLambdaPermissionForAuthorizer(functionName, region) {
_addLambdaPermissionForAuthorizer(authorizerId, functionName, region) {
let _this = this;
@ -499,7 +499,8 @@ module.exports = function(S) {
+ _this.provider.getAccountId(_this.evt.options.stage, region)
+ ':'
+ _this.restApiData.id
+ '/*';
+ '/authorizers/'
+ authorizerId;
return _this.provider.request('Lambda', 'addPermission', params, _this.evt.options.stage, region)
.then(function(data) {
@ -511,7 +512,7 @@ module.exports = function(S) {
+ region
+ '": '
+ 'added permission to Lambda for the authorizer');
});
})
}
/**

View File

@ -50,7 +50,7 @@ module.exports = function(S) {
shortcut: 'r',
description: 'Optional - Target one region to deploy to'
}, {
option: 'aliasFunction', // TODO: Implement
option: 'functionAlias', // TODO: Implement
shortcut: 'f',
description: 'Optional - Provide a custom Alias to your Functions'
}, {
@ -169,7 +169,7 @@ module.exports = function(S) {
_this.functions = [];
_this.evt.options.names = _this.evt.options.names ? _this.evt.options.names : [];
_this.evt.options.stage = _this.evt.options.stage ? _this.evt.options.stage : null;
_this.evt.options.aliasFunction = _this.evt.options.aliasFunction ? _this.evt.options.aliasFunction : null;
_this.evt.options.functionAlias = _this.evt.options.functionAlias ? _this.evt.options.functionAlias : null;
// Instantiate Classes
_this.project = S.getProject();
@ -279,11 +279,13 @@ module.exports = function(S) {
return S.actions.codePackageLambda(newEvt)
.bind(_this)
.then(function(result) {
let newEvt = {
options: {
stage: result.options.stage,
region: result.options.region,
name: func.name,
functionAlias: _this.evt.options.functionAlias,
pathDist: result.data.pathDist,
pathsPackaged: result.data.pathsPackaged
}
@ -297,12 +299,11 @@ module.exports = function(S) {
// Add Function and Region
if (!_this.deployed) _this.deployed = {};
if (!_this.deployed[region]) _this.deployed[region] = [];
_this.deployed[region].push({
lambdaName: result.data.functioName,
functionName: func.name,
Arn: result.data.lambdaAliasArn
Arn: result.data.functionAliasArn
});
return cb();

View File

@ -28,7 +28,10 @@ module.exports = function(S) {
constructor(config) {
super(config);
this.name = ProjectInit;
}
static getName() {
return 'serverless.core.' + this.name;
}
registerActions() {

View File

@ -9,7 +9,6 @@
* - Generates project JSON files
*
* Options:
* - name (String) a name for new project
* - profile (String) an AWS profile to create the project in. Must be available in ~/.aws/credentials
* - region (String) the first region for your new project
* - stage (String) the first stage for your new project
@ -19,13 +18,13 @@
module.exports = function(S) {
const path = require('path'),
SUtils = S.utils,
SError = require(S.getServerlessPath('Error')),
SCli = require(S.getServerlessPath('utils/cli')),
BbPromise = require('bluebird'),
fse = BbPromise.promisifyAll(require('fs-extra')),
_ = require('lodash');
const path = require('path'),
SUtils = S.utils,
SError = require(S.getServerlessPath('Error')),
SCli = require(S.getServerlessPath('utils/cli')),
BbPromise = require('bluebird'),
fse = BbPromise.promisifyAll(require('fs-extra')),
_ = require('lodash');
/**
* ProjectInstall Class
@ -38,17 +37,13 @@ module.exports = function(S) {
}
registerActions() {
S.addAction(this.installProject.bind(this), {
S.addAction(this.installProject.bind(this), {
handler: 'projectInstall',
description: 'Installs an existing Serverless project',
context: 'project',
contextAction: 'install',
options: [
{
option: 'name',
shortcut: 'n',
description: 'A new name for this Serverless project'
}, {
option: 'stage',
shortcut: 's',
description: 'Initial project stage'
@ -86,10 +81,10 @@ module.exports = function(S) {
*/
installProject(evt) {
// Greet
if (S.config.interactive) SCli.asciiGreeting();
let _this = this;
this.evt = evt;
@ -98,8 +93,8 @@ module.exports = function(S) {
return BbPromise.reject(new SError(`Please enter the name of the project you wish to install, like: serverless install project <projectname>`));
}
if (SUtils.dirExistsSync(path.join(process.cwd(), _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project))) {
return BbPromise.reject(new SError(`folder ${_this.evt.options.name ? _this.evt.options.name : _this.evt.options.project} already exists in the current directory`));
if (SUtils.dirExistsSync(path.join(process.cwd(), _this.evt.options.project))) {
return BbPromise.reject(new SError(`Folder ${_this.evt.options.project} already exists in the current directory`));
}
/**
@ -107,67 +102,26 @@ module.exports = function(S) {
*/
return BbPromise.try(function() {
console.log('');
SCli.log('Installing Serverless Project "' + _this.evt.options.project + '"...');
})
.bind(_this)
.then(_this._prompt)
.then(_this._installProject)
.then(_this._initProject)
.then(function() {
console.log('');
SCli.log('Installing Serverless Project "' + _this.evt.options.project + '"...');
})
.bind(_this)
.then(_this._installProject)
.then(_this._initProject)
.then(function() {
SCli.log('Successfully installed project "'
+ _this.evt.options.project
+ '"');
SCli.log('Successfully installed project "'
+ _this.evt.options.project
+ '"');
/**
* Return EVT
*/
/**
* Return EVT
*/
return _this.evt;
});
return _this.evt;
});
}
/**
* Prompt
*/
_prompt() {
// Set temp name
let name = this.evt.options.name || (this.evt.options.project + '-' + SUtils.generateShortId(6)).toLowerCase();
// Skip if non-interactive
if (!S.config.interactive) return BbPromise.resolve();
// Values that exist will not be prompted
let overrides = {
name: this.evt.options.name
};
let prompts = {
properties: {
name: {
description: 'Enter a name for this project: '.yellow,
default: name,
message: 'Name must be only letters, numbers or dashes',
required: true,
conform: (name) => {
let re = /^[a-zA-Z0-9-_]+$/;
// This hack updates the defaults in the other prompts
if (re.test(name)) this.evt.options.name = name;
return re.test(name);
}
}
}
};
return this.cliPromptInput(prompts, overrides)
.then((answers) => this.evt.options.name = answers.name);
}
/**
* Install Project
*/
@ -184,16 +138,20 @@ module.exports = function(S) {
const installPromise = this.evt.options.local ? this._installLocalProject() : this._installNpmProject();
return installPromise
.bind(this)
.then(() => this._spinner.stop(true))
.catch((e) => {
// Stop Spinner
this._spinner.stop(true);
console.error(e);
process.exit(1);
});
.bind(this)
.then(() => this._spinner.stop(true))
.catch((e) => {
// Stop Spinner
this._spinner.stop(true);
console.error(e);
process.exit(1);
});
}
/**
* Install NPM Project
*/
_installNpmProject() {
let _this = this;
@ -204,35 +162,37 @@ module.exports = function(S) {
fse.writeFileSync(path.join(process.cwd(), 'package.json'), JSON.stringify(packageJson, null, 2));
let exec = require('child_process').exec,
child;
child;
child = exec('npm install ' + _this.evt.options.project,
function (error, stdout, stderr) {
function (error, stdout, stderr) {
if (error !== null) return reject(new SError(error));
if (error !== null) return reject(new SError(error));
try {
fse.mkdirSync(path.join(process.cwd(), _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project));
fse.copySync(path.join(process.cwd(), 'node_modules', _this.evt.options.project), path.join(process.cwd(), _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project));
} catch (err) {
return reject(new SError(err))
}
try {
fse.mkdirSync(path.join(process.cwd(), _this.evt.options.project));
fse.copySync(path.join(process.cwd(), 'node_modules', _this.evt.options.project), path.join(process.cwd(), _this.evt.options.project));
} catch (err) {
return reject(new SError(err))
}
// Delete node_modules & package.json
fse.removeSync(path.join(process.cwd(), 'node_modules'));
fse.removeSync(path.join(process.cwd(), 'package.json'));
// Delete node_modules & package.json
fse.removeSync(path.join(process.cwd(), 'node_modules'));
fse.removeSync(path.join(process.cwd(), 'package.json'));
return resolve();
})
return resolve();
})
});
}
/**
* Install local Project
*/
_installLocalProject() {
const pathToProjectTemplate = path.resolve(process.cwd(), this.evt.options.local),
newFolderName = this.evt.options.name || this.evt.options.project,
pathToNewProject = path.join(process.cwd(), newFolderName);
newFolderName = this.evt.options.project,
pathToNewProject = path.join(process.cwd(), newFolderName);
return fse.copyAsync(pathToProjectTemplate, pathToNewProject);
}
@ -246,26 +206,25 @@ module.exports = function(S) {
let _this = this;
return BbPromise.try(function(){
// Update Global Serverless Instance
if( !S.hasProject() ) {
let projectPath = path.resolve(path.join(path.dirname('.'), _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project));
S.updateConfig({ projectPath: projectPath});
return S.init();
}
})
.then(function() {
return S.actions.projectInit({
options: {
name: _this.evt.options.name ? _this.evt.options.name : _this.evt.options.project,
stage: _this.evt.options.stage,
region: _this.evt.options.region,
profile: _this.evt.options.profile,
noExeCf: _this.evt.options.noExeCf,
noGreeting: true
}
});
});
// Update Global Serverless Instance
if( !S.hasProject() ) {
let projectPath = path.resolve(path.join(path.dirname('.'), _this.evt.options.project));
S.updateConfig({ projectPath: projectPath});
return S.init();
}
})
.then(function() {
return S.actions.projectInit({
options: {
name: _this.evt.options.project,
stage: _this.evt.options.stage,
region: _this.evt.options.region,
profile: _this.evt.options.profile,
noExeCf: _this.evt.options.noExeCf,
noGreeting: true
}
});
});
}
}

View File

@ -259,30 +259,6 @@ module.exports = {
return shortid.generate().replace(/\W+/g, '').substring(0, maxLen).replace(/[_-]/g, '');
},
/**
* Persistent Request
*/
persistentRequest: function(f) {
let _this = this;
return new BbPromise(function(resolve, reject){
let doCall = function(){
f()
.then(resolve)
.catch(function(error) {
if( error.statusCode == 429 ) {
_this.sDebug("'Too many requests' received, sleeping 5 seconds");
setTimeout( doCall, 5000 );
} else
reject( error );
});
};
return doCall();
});
},
/**
* Populate
*/

View File

@ -7,22 +7,22 @@ describe('All Tests', function() {
before(function() {});
after(function() {});
// require('./tests/classes/Project');
// require('./tests/classes/Function');
// require('./tests/classes/Endpoint');
// require('./tests/classes/Stage');
// require('./tests/classes/Region');
// require('./tests/actions/TestPluginCustom');
// require('./tests/actions/TestDefaultActionHook');
// require('./tests/actions/StageCreate');
// require('./tests/actions/RegionCreate');
// require('./tests/actions/FunctionCreate');
// require('./tests/actions/ResourcesDeploy');
// require('./tests/actions/FunctionRun');
// require('./tests/actions/FunctionLogs');
// require('./tests/actions/FunctionDeploy');
// require('./tests/actions/EndpointDeploy');
// require('./tests/actions/EventDeploy');
require('./tests/classes/Project');
require('./tests/classes/Function');
require('./tests/classes/Endpoint');
require('./tests/classes/Stage');
require('./tests/classes/Region');
require('./tests/actions/TestPluginCustom');
require('./tests/actions/TestDefaultActionHook');
require('./tests/actions/StageCreate');
require('./tests/actions/RegionCreate');
require('./tests/actions/FunctionCreate');
require('./tests/actions/ResourcesDeploy');
require('./tests/actions/FunctionRun');
require('./tests/actions/FunctionLogs');
require('./tests/actions/FunctionDeploy');
require('./tests/actions/EndpointDeploy');
require('./tests/actions/EventDeploy');
require('./tests/actions/ProjectInit');
require('./tests/actions/ProjectInstall');
require('./tests/actions/ResourcesDiff');

View File

@ -1,6 +1,41 @@
'use strict';
exports.handler = function(event, context) {
// Lambda Handler
module.exports.handler = function(event, context) {
return context.done(null, { message: '"functionFour" lambda function has run successfully' });
console.log('Running custom authorizer at ', new Date().toISOString() );
var token = event.authorizationToken;
token = 'allow'; // Auto-pass token for Serverless test
// Call oauth provider, crack jwt token, etc.
// In this example, the token is treated as the status for simplicity.
switch (token) {
case 'allow':
context.succeed(generatePolicy('user', 'Allow', event.methodArn));
break;
case 'deny':
context.succeed(generatePolicy('user', 'Deny', event.methodArn));
break;
case 'unauthorized':
context.fail("Unauthorized");
break;
default:
context.fail("error");
}
};
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17'; // default version
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke'; // default action
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
};

View File

@ -22,7 +22,7 @@ let serverless;
let validateEvent = function(evt) {
assert.equal(true, typeof evt.options.stage != 'undefined');
assert.equal(true, typeof evt.options.region != 'undefined');
assert.equal(true, typeof evt.options.aliasFunction != 'undefined');
assert.equal(true, typeof evt.options.functionAlias != 'undefined');
assert.equal(true, typeof evt.options.names != 'undefined');
if (evt.data.failed) {