Skip to content

Commit 2b9a0b5

Browse files
authored
Merge pull request #77 from HyperBrain/normalize-role-name
Normalize logical id of alias part in Lambda roles
2 parents f55cce8 + a952250 commit 2b9a0b5

30 files changed

+4459
-2070
lines changed

.eslintrc.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"plugin:import/errors",
1111
"plugin:import/warnings"
1212
],
13-
"installedESLint": true,
1413
"plugins": [
1514
"promise",
1615
"lodash",
@@ -19,7 +18,10 @@
1918
"rules": {
2019
"indent": [
2120
"error",
22-
"tab"
21+
"tab",
22+
{
23+
"MemberExpression": "off"
24+
}
2325
],
2426
"linebreak-style": [
2527
"error",

lib/configureAliasStack.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ module.exports = {
3333
*/
3434
this._serverless.service.provider
3535
.compiledCloudFormationAliasTemplate = this._serverless.utils.readFileSync(
36-
path.join(__dirname, 'alias-cloudformation-template.json')
37-
);
36+
path.join(__dirname, 'alias-cloudformation-template.json')
37+
);
3838

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

lib/createAliasStack.js

+9-9
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ module.exports = {
3636
};
3737

3838
return this._provider.request(
39-
'CloudFormation',
40-
'createStack',
41-
params,
42-
this._options.stage,
43-
this._options.region
44-
).then(cfData => this.monitorStack('create', cfData));
39+
'CloudFormation',
40+
'createStack',
41+
params,
42+
this._options.stage,
43+
this._options.region
44+
).then(cfData => this.monitorStack('create', cfData));
4545

4646
},
4747

@@ -60,9 +60,9 @@ module.exports = {
6060
}
6161

6262
return BbPromise.bind(this)
63-
// always write the template to disk, whether we are deploying or not
64-
.then(this.writeAliasTemplateToDisk)
65-
.then(this.checkAliasStack);
63+
// always write the template to disk, whether we are deploying or not
64+
.then(this.writeAliasTemplateToDisk)
65+
.then(this.checkAliasStack);
6666
},
6767

6868
checkAliasStack() {

lib/logs.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ module.exports = {
159159
if (this.options.tail) {
160160
return setTimeout((() => getLogStreams()
161161
.then(nextLogStreamNames => this.logsShowLogs(nextLogStreamNames, formatter, getLogStreams))),
162-
this.options.interval);
162+
this.options.interval);
163163
}
164164
}
165165

@@ -210,7 +210,7 @@ module.exports = {
210210

211211
return setTimeout((() => getLogStreams()
212212
.then(nextLogStreamNames => this.logsShowLogs(nextLogStreamNames, formatter, getLogStreams))),
213-
this.options.interval);
213+
this.options.interval);
214214
}
215215

216216
return BbPromise.resolve();

lib/removeAlias.js

+32-32
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ module.exports = {
1515
const usedFuncRefs = _.uniq(
1616
_.flatMap(aliasStackTemplates, template => {
1717
const funcRefs = _.map(
18-
_.assign({},
19-
_.pickBy(
20-
_.get(template, 'Resources', {}),
21-
[ 'Type', 'AWS::Lambda::Alias' ])),
22-
(value, key) => {
23-
return _.replace(key, /Alias$/, '');
24-
});
25-
18+
_.assign({},
19+
_.pickBy(
20+
_.get(template, 'Resources', {}),
21+
[ 'Type', 'AWS::Lambda::Alias' ])),
22+
(value, key) => {
23+
return _.replace(key, /Alias$/, '');
24+
}
25+
);
2626
return funcRefs;
2727
})
2828
);
@@ -143,7 +143,7 @@ module.exports = {
143143

144144
let stackTags = { STAGE: this._stage };
145145

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

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

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

172172
return this._provider.request('CloudFormation',
173-
'updateStack',
174-
params,
175-
this.options.stage,
176-
this.options.region)
177-
.then(cfData => this.monitorStack('update', cfData))
173+
'updateStack',
174+
params,
175+
this.options.stage,
176+
this.options.region)
177+
.then(cfData => this.monitorStack('update', cfData))
178178
.then(() => BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]))
179-
.catch(err => {
180-
if (err.message === NO_UPDATE_MESSAGE) {
181-
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
182-
}
183-
throw err;
184-
});
179+
.catch(err => {
180+
if (err.message === NO_UPDATE_MESSAGE) {
181+
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
182+
}
183+
throw err;
184+
});
185185

186186
},
187187

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

194194
return this._provider.request('CloudFormation',
195-
'deleteStack',
196-
{ StackName: stackName },
197-
this._options.stage,
198-
this._options.region)
195+
'deleteStack',
196+
{ StackName: stackName },
197+
this._options.stage,
198+
this._options.region)
199199
.then(cfData => {
200200
// monitorStack wants a StackId member
201201
cfData.StackId = stackName;
@@ -204,14 +204,14 @@ module.exports = {
204204
.then(() =>{
205205
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
206206
})
207-
.catch(e => {
208-
if (_.includes(e.message, 'does not exist')) {
209-
const message = `Alias ${this._alias} is not deployed.`;
210-
throw new this._serverless.classes.Error(message);
211-
}
207+
.catch(e => {
208+
if (_.includes(e.message, 'does not exist')) {
209+
const message = `Alias ${this._alias} is not deployed.`;
210+
throw new this._serverless.classes.Error(message);
211+
}
212212

213-
throw e;
214-
});
213+
throw e;
214+
});
215215

216216
},
217217

lib/stackops/apiGateway.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const internal = {
3737
const stageResource = {
3838
Type: 'AWS::ApiGateway::Stage',
3939
Properties: {
40-
StageName: this._alias,
40+
StageName: _.replace(this._alias, /-/g, '_'),
4141
DeploymentId: {
4242
Ref: deploymentName
4343
},
@@ -192,7 +192,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
192192
const apiLambdaPermissions =
193193
_.assign({},
194194
_.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]),
195-
['Properties.Principal', 'apigateway.amazonaws.com']));
195+
['Properties.Principal', 'apigateway.amazonaws.com']));
196196

197197
const apiMethods = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Method' ]));
198198
const authorizers = _.assign({}, _.pickBy(stageStack.Resources, [ 'Type', 'AWS::ApiGateway::Authorizer' ]));

lib/stackops/cwEvents.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
1616
const cwEventLambdaPermissions =
1717
_.assign({},
1818
_.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]),
19-
['Properties.Principal', 'events.amazonaws.com']));
19+
['Properties.Principal', 'events.amazonaws.com']));
2020

2121
_.forOwn(cwEvents, (cwEvent, name) => {
2222
// Reference alias as FunctionName

lib/stackops/events.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
5757
delete stageStack.Resources[name];
5858
});
5959

60-
// Move event subscriptions to alias stack
60+
// Move event subscriptions to alias stack
6161
_.defaults(aliasStack.Resources, subscriptions);
6262

63-
// Forward inputs to the promise chain
63+
// Forward inputs to the promise chain
6464
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
6565
};

lib/stackops/functions.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ function mergeAliases(stackName, newTemplate, currentTemplate, aliasStackTemplat
3939
// Get currently deployed function definitions and versions and retain them in the stack update
4040
const usedFunctionElements = {
4141
Resources: _.map(aliasedFunctions, aliasedFunction => _.assign(
42-
{},
43-
_.pick(currentTemplate.Resources, [ aliasedFunction.name, aliasedFunction.version ])
44-
)),
42+
{},
43+
_.pick(currentTemplate.Resources, [ aliasedFunction.name, aliasedFunction.version ])
44+
)),
4545
Outputs: _.map(aliasedFunctions, aliasedFunction => _.assign(
46-
{},
47-
_.pick(currentTemplate.Outputs, [ `${aliasedFunction.name}Arn`, aliasedFunction.version ])
48-
))
46+
{},
47+
_.pick(currentTemplate.Outputs, [ `${aliasedFunction.name}Arn`, aliasedFunction.version ])
48+
))
4949
};
5050

5151
_.forEach(usedFunctionElements.Resources, resources => _.defaults(newTemplate.Resources, resources));

lib/stackops/lambdaRole.js

+10-7
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,28 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
2929
return BbPromise.resolve([ currentTemplate, aliasStackTemplates, currentAliasStackTemplate ]);
3030
}
3131

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

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

38-
stageStack.Resources[roleName] = stageStack.Resources.IamRoleLambdaExecution;
40+
stageStack.Resources[roleLogicalId] = stageStack.Resources.IamRoleLambdaExecution;
3941
delete stageStack.Resources.IamRoleLambdaExecution;
4042

4143
// Replace references
4244
const functions = _.filter(stageStack.Resources, ['Type', 'AWS::Lambda::Function']);
4345
_.forEach(functions, func => {
4446
func.Properties.Role = {
4547
'Fn::GetAtt': [
46-
roleName,
48+
roleLogicalId,
4749
'Arn'
4850
]
4951
};
5052
const dependencyIndex = _.indexOf(func.DependsOn, 'IamRoleLambdaExecution');
51-
func.DependsOn[dependencyIndex] = roleName;
53+
func.DependsOn[dependencyIndex] = roleLogicalId;
5254
});
5355

5456
if (_.has(currentTemplate, 'Resources.IamRoleLambdaExecution')) {
@@ -61,10 +63,11 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
6163
// Retain the roles of all currently deployed aliases
6264
_.forEach(aliasStackTemplates, aliasTemplate => {
6365
const alias = _.get(aliasTemplate, 'Outputs.ServerlessAliasName.Value');
64-
const aliasRoleName = `IamRoleLambdaExecution${alias}`;
65-
const aliasRole = _.get(currentTemplate, `Resources.${aliasRoleName}`);
66+
const aliasNormalizedAlias = utils.normalizeAliasForLogicalId(alias);
67+
const aliasRoleLogicalId = `IamRoleLambdaExecution${aliasNormalizedAlias}`;
68+
const aliasRole = _.get(currentTemplate, `Resources.${aliasRoleLogicalId}`);
6669
if (alias && aliasRole) {
67-
stageStack.Resources[aliasRoleName] = aliasRole;
70+
stageStack.Resources[aliasRoleLogicalId] = aliasRole;
6871
}
6972
});
7073

lib/stackops/snsEvents.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ module.exports = function(currentTemplate, aliasStackTemplates, currentAliasStac
5050
const snsLambdaPermissions =
5151
_.assign({},
5252
_.pickBy(_.pickBy(stageStack.Resources, [ 'Type', 'AWS::Lambda::Permission' ]),
53-
[ 'Properties.Principal', 'sns.amazonaws.com' ]));
53+
[ 'Properties.Principal', 'sns.amazonaws.com' ]));
5454

5555
// Adjust permission to reference the function aliases
5656
_.forOwn(snsLambdaPermissions, (permission, name) => {

lib/updateAliasStack.js

+2-10
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ module.exports = {
1414

1515
const stackName = `${this._provider.naming.getStackName()}-${this._alias}`;
1616
let stackTags = { STAGE: this._options.stage, ALIAS: this._alias };
17-
const templateUrl = `https://s3.amazonaws.com/${
18-
this.bucketName
19-
}/${
20-
this._serverless.service.package.artifactDirectoryName
21-
}/compiled-cloudformation-template-alias.json`;
17+
const templateUrl = `https://s3.amazonaws.com/${this.bucketName}/${this._serverless.service.package.artifactDirectoryName}/compiled-cloudformation-template-alias.json`;
2218
// Merge additional stack tags
2319
if (_.isObject(this._serverless.service.provider.stackTags)) {
2420
stackTags = _.extend(stackTags, this._serverless.service.provider.stackTags);
@@ -49,11 +45,7 @@ module.exports = {
4945
},
5046

5147
updateAlias() {
52-
const templateUrl = `https://s3.amazonaws.com/${
53-
this.bucketName
54-
}/${
55-
this._serverless.service.package.artifactDirectoryName
56-
}/compiled-cloudformation-template-alias.json`;
48+
const templateUrl = `https://s3.amazonaws.com/${this.bucketName}/${this._serverless.service.package.artifactDirectoryName}/compiled-cloudformation-template-alias.json`;
5749

5850
this.serverless.cli.log('Updating alias stack...');
5951
const stackName = `${this._provider.naming.getStackName()}-${this._alias}`;

lib/updateFunctionAlias.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

3-
const BbPromise = require("bluebird");
3+
const BbPromise = require('bluebird');
4+
const _ = require('lodash');
45

56
module.exports = {
67

@@ -15,7 +16,7 @@ module.exports = {
1516
// Get the hash of the deployed function package
1617
const params = {
1718
FunctionName: func.name,
18-
Qualifier: "$LATEST"
19+
Qualifier: '$LATEST'
1920
};
2021

2122
return this.provider.request(
@@ -31,7 +32,7 @@ module.exports = {
3132
const params = {
3233
FunctionName: func.name,
3334
CodeSha256: sha256,
34-
Description: "Deployed manually"
35+
Description: 'Deployed manually'
3536
};
3637
return this.provider.request(
3738
'Lambda',
@@ -57,7 +58,17 @@ module.exports = {
5758
);
5859
})
5960
.then(result => {
60-
this.serverless.cli.log(`Successfully updated alias: ${this.options.function}@${this._alias} -> ${result.FunctionVersion}`);
61+
this.serverless.cli.log(_.join(
62+
[
63+
'Successfully updated alias: ',
64+
this.options.function,
65+
'@',
66+
this._alias,
67+
' -> ',
68+
result.FunctionVersion
69+
],
70+
''
71+
));
6172
return BbPromise.resolve();
6273
});
6374
}

0 commit comments

Comments
 (0)