Skip to content

Commit

Permalink
feat: support local function names instead of logical id
Browse files Browse the repository at this point in the history
  • Loading branch information
theburningmonk committed Jul 30, 2019
1 parent 03d4f71 commit b9525e5
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 14 deletions.
20 changes: 10 additions & 10 deletions lib/deploy/stepFunctions/compileIamRole.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const _ = require('lodash');
const BbPromise = require('bluebird');
const path = require('path');
const { isIntrinsic } = require('../../utils/aws');
const { isIntrinsic, translateLocalFunctionNames } = require('../../utils/aws');

function getTaskStates(states) {
return _.flatMap(states, (state) => {
Expand Down Expand Up @@ -177,7 +177,7 @@ function getLambdaPermissions(state) {
// so you should be able to use Fn::GetAtt here to get the ARN
return [{
action: 'lambda:InvokeFunction',
resource: functionName,
resource: translateLocalFunctionNames.bind(this)(functionName),
}];
} if (_.has(functionName, 'Ref')) {
// because the FunctionName parameter can be either a name or ARN
Expand All @@ -188,7 +188,7 @@ function getLambdaPermissions(state) {
'Fn::Sub': [
'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${FunctionName}',
{
FunctionName: functionName,
FunctionName: translateLocalFunctionNames.bind(this)(functionName),
},
],
},
Expand Down Expand Up @@ -239,16 +239,16 @@ function consolidatePermissionsByResource(permissions) {
.value(); // unchain
}

function getIamPermissions(serverless, taskStates) {
function getIamPermissions(taskStates) {
return _.flatMap(taskStates, (state) => {
switch (state.Resource) {
case 'arn:aws:states:::sqs:sendMessage':
case 'arn:aws:states:::sqs:sendMessage.waitForTaskToken':
return getSqsPermissions(serverless, state);
return getSqsPermissions(this.serverless, state);

case 'arn:aws:states:::sns:publish':
case 'arn:aws:states:::sns:publish.waitForTaskToken':
return getSnsPermissions(serverless, state);
return getSnsPermissions(this.serverless, state);

case 'arn:aws:states:::dynamodb:updateItem':
return getDynamoDBPermissions('dynamodb:UpdateItem', state);
Expand All @@ -274,16 +274,16 @@ function getIamPermissions(serverless, taskStates) {

case 'arn:aws:states:::lambda:invoke':
case 'arn:aws:states:::lambda:invoke.waitForTaskToken':
return getLambdaPermissions(state);
return getLambdaPermissions.bind(this)(state);

default:
if (isIntrinsic(state.Resource) || state.Resource.startsWith('arn:aws:lambda')) {
return [{
action: 'lambda:InvokeFunction',
resource: state.Resource,
resource: translateLocalFunctionNames.bind(this)(state.Resource),
}];
}
serverless.cli.consoleLog('Cannot generate IAM policy statement for Task state', state);
this.serverless.cli.consoleLog('Cannot generate IAM policy statement for Task state', state);
return [];
}
});
Expand Down Expand Up @@ -317,7 +317,7 @@ module.exports = {
customRolesProvided.push('role' in stateMachineObj);

const taskStates = getTaskStates(stateMachineObj.definition.States);
iamPermissions = iamPermissions.concat(getIamPermissions(this.serverless, taskStates));
iamPermissions = iamPermissions.concat(getIamPermissions.bind(this)(taskStates));
});
if (_.isEqual(_.uniq(customRolesProvided), [true])) {
return BbPromise.resolve();
Expand Down
12 changes: 8 additions & 4 deletions lib/deploy/stepFunctions/compileStateMachines.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const _ = require('lodash');
const BbPromise = require('bluebird');
const Chance = require('chance');
const { isIntrinsic } = require('../../utils/aws');
const { isIntrinsic, translateLocalFunctionNames } = require('../../utils/aws');

const chance = new Chance();

Expand Down Expand Up @@ -83,13 +83,17 @@ module.exports = {
.replace(/\\n|\\r|\\n\\r/g, '');
} else {
const functionMappings = Array.from(getIntrinsicFunctions(stateMachineObj.definition));
const definitionString = JSON.stringify(stateMachineObj.definition, undefined, 2);

if (_.isEmpty(functionMappings)) {
DefinitionString = JSON.stringify(stateMachineObj.definition, undefined, 2);
DefinitionString = definitionString;
} else {
const f = translateLocalFunctionNames.bind(this);
const params = _.fromPairs(functionMappings.map(([k, v]) => [k, f(v)]));
DefinitionString = {
'Fn::Sub': [
JSON.stringify(stateMachineObj.definition, undefined, 2),
_.fromPairs(functionMappings),
definitionString,
params,
],
};
}
Expand Down
33 changes: 33 additions & 0 deletions lib/utils/aws.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ function isIntrinsic(obj) {
&& Object.keys(obj).some(k => k.startsWith('Fn::') || k === 'Ref');
}

// translates local function names, e.g. hello-world
// to logical resource name, e.g. HelloDashworldLambdaFunction
function translateLocalFunctionNames(value) {
const resources = this.serverless.service.provider.compiledCloudFormationTemplate.Resources;
const functions = this.serverless.service.functions;

const translate = (logicalId) => {
if (!_.has(resources, logicalId) && _.has(functions, logicalId)) {
const functionName = logicalId;
return this.provider.naming.getLambdaLogicalId(functionName);
}

return logicalId;
};

if (_.has(value, 'Ref')) {
const logicalId = value.Ref;
return {
Ref: translate(logicalId),
};
}

if (_.has(value, 'Fn::GetAtt')) {
const [logicalId, prop] = value['Fn::GetAtt'];
return {
'Fn::GetAtt': [translate(logicalId), prop],
};
}

return value;
}

module.exports = {
isIntrinsic,
translateLocalFunctionNames,
};

0 comments on commit b9525e5

Please sign in to comment.