Skip to content

Commit

Permalink
Merge 7927148 into 7fbe862
Browse files Browse the repository at this point in the history
  • Loading branch information
laardee committed Oct 18, 2019
2 parents 7fbe862 + 7927148 commit 96a1bbe
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 4 deletions.
4 changes: 4 additions & 0 deletions docs/providers/aws/events/streams.md
Expand Up @@ -47,6 +47,10 @@ functions:
type: kinesis
arn:
Fn::ImportValue: MyExportedKinesisStreamArnId
- stream:
type: dynamodb
arn:
Ref: MyDynamoDbTableStreamArn
- stream:
type: kinesis
arn:
Expand Down
12 changes: 8 additions & 4 deletions lib/plugins/aws/package/compile/events/stream/index.js
Expand Up @@ -72,13 +72,15 @@ class AwsCompileStreamEvents {
!(
_.has(event.stream.arn, 'Fn::ImportValue') ||
_.has(event.stream.arn, 'Fn::GetAtt') ||
(_.has(event.stream.arn, 'Ref') &&
_.has(this.serverless.service.resources.Parameters, event.stream.arn.Ref)) ||
_.has(event.stream.arn, 'Fn::Join')
)
) {
const errorMessage = [
`Bad dynamic ARN property on stream event in function "${functionName}"`,
' If you use a dynamic "arn" (such as with Fn::GetAtt, Fn::Join',
' or Fn::ImportValue) there must only be one key (either Fn::GetAtt, Fn::Join',
' If you use a dynamic "arn" (such as with Fn::GetAtt, Fn::Join, Ref',
' or Fn::ImportValue) there must only be one key (either Fn::GetAtt, Fn::Join, Ref',
' or Fn::ImportValue) in the arn object. Please check the docs for more info.',
].join('');
throw new this.serverless.classes.Error(errorMessage);
Expand Down Expand Up @@ -108,6 +110,8 @@ class AwsCompileStreamEvents {
return EventSourceArn['Fn::GetAtt'][0];
} else if (EventSourceArn['Fn::ImportValue']) {
return EventSourceArn['Fn::ImportValue'];
} else if (EventSourceArn.Ref) {
return EventSourceArn.Ref;
} else if (EventSourceArn['Fn::Join']) {
// [0] is the used delimiter, [1] is the array with values
const name = EventSourceArn['Fn::Join'][1].slice(-1).pop();
Expand Down Expand Up @@ -147,9 +151,9 @@ class AwsCompileStreamEvents {
) {
dependsOn = `"${funcRole['Fn::GetAtt'][0]}"`;
} else if (
// otherwise, check if we have an import
// otherwise, check if we have an import or parameters ref
typeof funcRole === 'object' &&
'Fn::ImportValue' in funcRole
('Fn::ImportValue' in funcRole || 'Ref' in funcRole)
) {
dependsOn = '[]';
} else if (typeof funcRole === 'string') {
Expand Down
106 changes: 106 additions & 0 deletions lib/plugins/aws/package/compile/events/stream/index.test.js
Expand Up @@ -234,6 +234,35 @@ describe('AwsCompileStreamEvents', () => {
).to.equal(null);
});

it('should not throw error if IAM role is referenced from cloudformation parameters', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
role: { Ref: 'MyStreamRoleArn' },
events: [
{
// doesn't matter if DynamoDB or Kinesis stream
stream: 'arn:aws:dynamodb:region:account:table/foo/stream/1',
},
],
},
};

// pretend that the default IamRoleLambdaExecution is not in place
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources.IamRoleLambdaExecution = null;

expect(() => {
awsCompileStreamEvents.compileStreamEvents();
}).to.not.throw(Error);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.FirstEventSourceMappingDynamodbFoo.DependsOn.length
).to.equal(0);
expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
.IamRoleLambdaExecution
).to.equal(null);
});

it('should not throw error if IAM role is imported', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
Expand Down Expand Up @@ -444,6 +473,14 @@ describe('AwsCompileStreamEvents', () => {
});

it('should allow specifying DynamoDB and Kinesis streams as CFN reference types', () => {
awsCompileStreamEvents.serverless.service.resources.Parameters = {
SomeDdbTableStreamArn: {
Type: 'String',
},
ForeignKinesisStreamArn: {
Type: 'String',
},
};
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
Expand Down Expand Up @@ -481,6 +518,18 @@ describe('AwsCompileStreamEvents', () => {
type: 'kinesis',
},
},
{
stream: {
arn: { Ref: 'SomeDdbTableStreamArn' },
type: 'dynamodb',
},
},
{
stream: {
arn: { Ref: 'ForeignKinesisStreamArn' },
type: 'kinesis',
},
},
],
},
};
Expand All @@ -507,6 +556,9 @@ describe('AwsCompileStreamEvents', () => {
{
'Fn::GetAtt': ['SomeDdbTable', 'StreamArn'],
},
{
Ref: 'SomeDdbTableStreamArn',
},
],
});

Expand Down Expand Up @@ -537,6 +589,60 @@ describe('AwsCompileStreamEvents', () => {
],
],
});

expect(
awsCompileStreamEvents.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement[1]
).to.deep.equal({
Effect: 'Allow',
Action: [
'kinesis:GetRecords',
'kinesis:GetShardIterator',
'kinesis:DescribeStream',
'kinesis:ListStreams',
],
Resource: [
{
'Fn::ImportValue': 'ForeignKinesis',
},
{
'Fn::Join': [
':',
[
'arn',
'aws',
'kinesis',
{
Ref: 'AWS::Region',
},
{
Ref: 'AWS::AccountId',
},
'stream/MyStream',
],
],
},
{
Ref: 'ForeignKinesisStreamArn',
},
],
});
});

it('fails if Ref/dynamic stream ARN is used without defining it to the CF parameters', () => {
awsCompileStreamEvents.serverless.service.functions = {
first: {
events: [
{
stream: {
arn: { Ref: 'SomeDdbTableStreamArn' },
},
},
],
},
};

expect(() => awsCompileStreamEvents.compileStreamEvents()).to.throw(Error);
});

it('fails if Fn::GetAtt/dynamic stream ARN is used without a type', () => {
Expand Down

0 comments on commit 96a1bbe

Please sign in to comment.