Skip to content

Commit

Permalink
Support _ - and + in alias names
Browse files Browse the repository at this point in the history
Turn hyphens to underscores in stage names

Added unit tests for normalization

Fixed regex.

Updated dependencies and test framework.

Fixed ESLint

Normalize logical id of alias part in Lambda roles
  • Loading branch information
Frank Schmid committed Nov 22, 2017
1 parent e0215d2 commit a952250
Show file tree
Hide file tree
Showing 31 changed files with 4,460 additions and 2,070 deletions.
6 changes: 4 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"plugin:import/errors",
"plugin:import/warnings"
],
"installedESLint": true,
"plugins": [
"promise",
"lodash",
Expand All @@ -19,7 +18,10 @@
"rules": {
"indent": [
"error",
"tab"
"tab",
{
"MemberExpression": "off"
}
],
"linebreak-style": [
"error",
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/node_modules
/coverage
/.nyc_output
4 changes: 2 additions & 2 deletions lib/configureAliasStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ module.exports = {
*/
this._serverless.service.provider
.compiledCloudFormationAliasTemplate = this._serverless.utils.readFileSync(
path.join(__dirname, 'alias-cloudformation-template.json')
);
path.join(__dirname, 'alias-cloudformation-template.json')
);

const aliasTemplate = this._serverless.service.provider.compiledCloudFormationAliasTemplate;

Expand Down
18 changes: 9 additions & 9 deletions lib/createAliasStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ module.exports = {
};

return this._provider.request(
'CloudFormation',
'createStack',
params,
this._options.stage,
this._options.region
).then(cfData => this.monitorStack('create', cfData));
'CloudFormation',
'createStack',
params,
this._options.stage,
this._options.region
).then(cfData => this.monitorStack('create', cfData));

},

Expand All @@ -60,9 +60,9 @@ module.exports = {
}

return BbPromise.bind(this)
// always write the template to disk, whether we are deploying or not
.then(this.writeAliasTemplateToDisk)
.then(this.checkAliasStack);
// always write the template to disk, whether we are deploying or not
.then(this.writeAliasTemplateToDisk)
.then(this.checkAliasStack);
},

checkAliasStack() {
Expand Down
4 changes: 2 additions & 2 deletions lib/logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ module.exports = {
if (this.options.tail) {
return setTimeout((() => getLogStreams()
.then(nextLogStreamNames => this.logsShowLogs(nextLogStreamNames, formatter, getLogStreams))),
this.options.interval);
this.options.interval);
}
}

Expand Down Expand Up @@ -210,7 +210,7 @@ module.exports = {

return setTimeout((() => getLogStreams()
.then(nextLogStreamNames => this.logsShowLogs(nextLogStreamNames, formatter, getLogStreams))),
this.options.interval);
this.options.interval);
}

return BbPromise.resolve();
Expand Down
64 changes: 32 additions & 32 deletions lib/removeAlias.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ module.exports = {
const usedFuncRefs = _.uniq(
_.flatMap(aliasStackTemplates, template => {
const funcRefs = _.map(
_.assign({},
_.pickBy(
_.get(template, 'Resources', {}),
[ 'Type', 'AWS::Lambda::Alias' ])),
(value, key) => {
return _.replace(key, /Alias$/, '');
});

_.assign({},
_.pickBy(
_.get(template, 'Resources', {}),
[ 'Type', 'AWS::Lambda::Alias' ])),
(value, key) => {
return _.replace(key, /Alias$/, '');
}
);
return funcRefs;
})
);
Expand Down Expand Up @@ -143,7 +143,7 @@ module.exports = {

let stackTags = { STAGE: this._stage };

// Merge additional stack tags
// Merge additional stack tags
if (_.isObject(this.serverless.service.provider.stackTags)) {
stackTags = _.extend(stackTags, this.serverless.service.provider.stackTags);
}
Expand All @@ -161,7 +161,7 @@ module.exports = {

this.options.verbose && this._serverless.cli.log(`Checking stack policy`);

// Policy must have at least one statement, otherwise no updates would be possible at all
// Policy must have at least one statement, otherwise no updates would be possible at all
if (this.serverless.service.provider.stackPolicy &&
this.serverless.service.provider.stackPolicy.length) {
params.StackPolicyBody = JSON.stringify({
Expand All @@ -170,18 +170,18 @@ module.exports = {
}

return this._provider.request('CloudFormation',
'updateStack',
params,
this.options.stage,
this.options.region)
.then(cfData => this.monitorStack('update', cfData))
'updateStack',
params,
this.options.stage,
this.options.region)
.then(cfData => this.monitorStack('update', cfData))
.then(() => BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]))
.catch(err => {
if (err.message === NO_UPDATE_MESSAGE) {
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
}
throw err;
});
.catch(err => {
if (err.message === NO_UPDATE_MESSAGE) {
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
}
throw err;
});

},

Expand All @@ -192,10 +192,10 @@ module.exports = {
this.options.verbose && this._serverless.cli.log(`Removing CF stack ${stackName}`);

return this._provider.request('CloudFormation',
'deleteStack',
{ StackName: stackName },
this._options.stage,
this._options.region)
'deleteStack',
{ StackName: stackName },
this._options.stage,
this._options.region)
.then(cfData => {
// monitorStack wants a StackId member
cfData.StackId = stackName;
Expand All @@ -204,14 +204,14 @@ module.exports = {
.then(() =>{
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
})
.catch(e => {
if (_.includes(e.message, 'does not exist')) {
const message = `Alias ${this._alias} is not deployed.`;
throw new this._serverless.classes.Error(message);
}
.catch(e => {
if (_.includes(e.message, 'does not exist')) {
const message = `Alias ${this._alias} is not deployed.`;
throw new this._serverless.classes.Error(message);
}

throw e;
});
throw e;
});

},

Expand Down
4 changes: 2 additions & 2 deletions lib/stackops/apiGateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const internal = {
const stageResource = {
Type: 'AWS::ApiGateway::Stage',
Properties: {
StageName: this._alias,
StageName: _.replace(this._alias, /-/g, '_'),
DeploymentId: {
Ref: deploymentName
},
Expand Down Expand Up @@ -180,7 +180,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
const apiLambdaPermissions =
_.assign({},
_.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]),
['Properties.Principal', 'apigateway.amazonaws.com']));
['Properties.Principal', 'apigateway.amazonaws.com']));

const apiMethods = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Method' ]));
const authorizers = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Authorizer' ]));
Expand Down
2 changes: 1 addition & 1 deletion lib/stackops/cwEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
const cwEventLambdaPermissions =
_.assign({},
_.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]),
['Properties.Principal', 'events.amazonaws.com']));
['Properties.Principal', 'events.amazonaws.com']));

_.forOwn(cwEvents, (cwEvent, name) => {
// Reference alias as FunctionName
Expand Down
4 changes: 2 additions & 2 deletions lib/stackops/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
delete stageStack.Resources[name];
});

// Move event subscriptions to alias stack
// Move event subscriptions to alias stack
_.defaults(aliasStack.Resources, subscriptions);

// Forward inputs to the promise chain
// Forward inputs to the promise chain
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
};
12 changes: 6 additions & 6 deletions lib/stackops/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ function mergeAliases(stackName, newTemplate, currentTemplate, aliasStackTemplat
// Get currently deployed function definitions and versions and retain them in the stack update
const usedFunctionElements = {
Resources: _.map(aliasedFunctions, aliasedFunction => _.assign(
{},
_.pick(currentTemplate.Resources, [ aliasedFunction.name, aliasedFunction.version ])
)),
{},
_.pick(currentTemplate.Resources, [ aliasedFunction.name, aliasedFunction.version ])
)),
Outputs: _.map(aliasedFunctions, aliasedFunction => _.assign(
{},
_.pick(currentTemplate.Outputs, [ `${aliasedFunction.name}Arn`, aliasedFunction.version ])
))
{},
_.pick(currentTemplate.Outputs, [ `${aliasedFunction.name}Arn`, aliasedFunction.version ])
))
};

_.forEach(usedFunctionElements.Resources, resources => _.defaults(newTemplate.Resources, resources));
Expand Down
17 changes: 10 additions & 7 deletions lib/stackops/lambdaRole.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,28 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
}

const roleName = `IamRoleLambdaExecution${this._alias}`;
// Role name allows [\w+=,.@-]+
const normalizedAlias = utils.normalizeAliasForLogicalId(this._alias);
const roleLogicalId = `IamRoleLambdaExecution${normalizedAlias}`;
const role = stageStack.Resources.IamRoleLambdaExecution;

// Set role name
_.last(role.Properties.RoleName['Fn::Join']).push(this._alias);

stageStack.Resources[roleName] = stageStack.Resources.IamRoleLambdaExecution;
stageStack.Resources[roleLogicalId] = stageStack.Resources.IamRoleLambdaExecution;
delete stageStack.Resources.IamRoleLambdaExecution;

// Replace references
const functions = _.filter(stageStack.Resources, ['Type', 'AWS::Lambda::Function']);
_.forEach(functions, func => {
func.Properties.Role = {
'Fn::GetAtt': [
roleName,
roleLogicalId,
'Arn'
]
};
const dependencyIndex = _.indexOf(func.DependsOn, 'IamRoleLambdaExecution');
func.DependsOn[dependencyIndex] = roleName;
func.DependsOn[dependencyIndex] = roleLogicalId;
});

if (_.has(currentTemplate, 'Resources.IamRoleLambdaExecution')) {
Expand All @@ -61,10 +63,11 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
// Retain the roles of all currently deployed aliases
_.forEach(aliasStackTemplates, aliasTemplate => {
const alias = _.get(aliasTemplate, 'Outputs.ServerlessAliasName.Value');
const aliasRoleName = `IamRoleLambdaExecution${alias}`;
const aliasRole = _.get(currentTemplate, `Resources.${aliasRoleName}`);
const aliasNormalizedAlias = utils.normalizeAliasForLogicalId(alias);
const aliasRoleLogicalId = `IamRoleLambdaExecution${aliasNormalizedAlias}`;
const aliasRole = _.get(currentTemplate, `Resources.${aliasRoleLogicalId}`);
if (alias && aliasRole) {
stageStack.Resources[aliasRoleName] = aliasRole;
stageStack.Resources[aliasRoleLogicalId] = aliasRole;
}
});

Expand Down
2 changes: 1 addition & 1 deletion lib/stackops/snsEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
const snsLambdaPermissions =
_.assign({},
_.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]),
[ 'Properties.Principal', 'sns.amazonaws.com' ]));
[ 'Properties.Principal', 'sns.amazonaws.com' ]));

// Adjust permission to reference the function aliases
_.forOwn(snsLambdaPermissions, (permission, name) => {
Expand Down
12 changes: 2 additions & 10 deletions lib/updateAliasStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ module.exports = {

const stackName = `${this._provider.naming.getStackName()}-${this._alias}`;
let stackTags = { STAGE: this._options.stage, ALIAS: this._alias };
const templateUrl = `https://s3.amazonaws.com/${
this.bucketName
}/${
this._serverless.service.package.artifactDirectoryName
}/compiled-cloudformation-template-alias.json`;
const templateUrl = `https://s3.amazonaws.com/${this.bucketName}/${this._serverless.service.package.artifactDirectoryName}/compiled-cloudformation-template-alias.json`;
// Merge additional stack tags
if (_.isObject(this._serverless.service.provider.stackTags)) {
stackTags = _.extend(stackTags, this._serverless.service.provider.stackTags);
Expand Down Expand Up @@ -49,11 +45,7 @@ module.exports = {
},

updateAlias() {
const templateUrl = `https://s3.amazonaws.com/${
this.bucketName
}/${
this._serverless.service.package.artifactDirectoryName
}/compiled-cloudformation-template-alias.json`;
const templateUrl = `https://s3.amazonaws.com/${this.bucketName}/${this._serverless.service.package.artifactDirectoryName}/compiled-cloudformation-template-alias.json`;

this.serverless.cli.log('Updating alias stack...');
const stackName = `${this._provider.naming.getStackName()}-${this._alias}`;
Expand Down
19 changes: 15 additions & 4 deletions lib/updateFunctionAlias.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const BbPromise = require("bluebird");
const BbPromise = require('bluebird');
const _ = require('lodash');

module.exports = {

Expand All @@ -15,7 +16,7 @@ module.exports = {
// Get the hash of the deployed function package
const params = {
FunctionName: func.name,
Qualifier: "$LATEST"
Qualifier: '$LATEST'
};

return this.provider.request(
Expand All @@ -31,7 +32,7 @@ module.exports = {
const params = {
FunctionName: func.name,
CodeSha256: sha256,
Description: "Deployed manually"
Description: 'Deployed manually'
};
return this.provider.request(
'Lambda',
Expand All @@ -57,7 +58,17 @@ module.exports = {
);
})
.then(result => {
this.serverless.cli.log(`Successfully updated alias: ${this.options.function}@${this._alias} -> ${result.FunctionVersion}`);
this.serverless.cli.log(_.join(
[
'Successfully updated alias: ',
this.options.function,
'@',
this._alias,
' -> ',
result.FunctionVersion
],
''
));
return BbPromise.resolve();
});
}
Expand Down
Loading

0 comments on commit a952250

Please sign in to comment.