Skip to content

Commit

Permalink
Merge pull request #77 from HyperBrain/normalize-role-name
Browse files Browse the repository at this point in the history
Normalize logical id of alias part in Lambda roles
  • Loading branch information
HyperBrain authored Nov 22, 2017
2 parents f55cce8 + a952250 commit 2b9a0b5
Show file tree
Hide file tree
Showing 30 changed files with 4,459 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
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 @@ -192,7 +192,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 2b9a0b5

Please sign in to comment.