From 64a203f05a107f76e28b88f569d5dda993f1cb5a Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Thu, 27 Jun 2019 16:07:49 +0300 Subject: [PATCH] fix(sns): create subscriptions in consumer scope (#3065) Since in most cases the consumer needs to reference the topic to permit it to send them messages (e.g. invoke a lambda function or send messages to the queue), it makes much more send to create the SNS subscription resource on the consumer's scope/stack instead of the topic's. This change adds an optional scope field to TopicSubscriptionConfig which is respected by topic.addSubscription. If scope is not defined, the topic's scope will be used. We also changed subscriberId to be optional, since in the case where scope is specified, the natural ID for the subscription construct would be the topic's unique ID, which is now the default. A runtime error will be emitted if both scope and subscriberId are not provided. Fixes #3064 --- ...integ.scheduled-ecs-task.lit.expected.json | 40 +- .../test/ec2/integ.lb-awsvpc-nw.expected.json | 48 +-- .../test/ec2/integ.lb-bridge-nw.expected.json | 48 +-- .../test/ec2/integ.sd-awsvpc-nw.expected.json | 50 +-- .../test/ec2/integ.sd-bridge-nw.expected.json | 50 +-- .../integ.project-events.expected.json | 30 +- .../integ.event-ec2-task.lit.expected.json | 40 +- .../integ.sns-event-rule-target.expected.json | 32 +- .../test/integ.sns.expected.json | 32 +- .../aws-sns-subscriptions/lib/lambda.ts | 5 +- .../@aws-cdk/aws-sns-subscriptions/lib/sqs.ts | 3 +- .../test/integ.sns-lambda.expected.json | 122 +++---- .../test/integ.sns-sqs.lit.expected.json | 30 +- .../aws-sns-subscriptions/test/subs.test.ts | 343 ++++++++++-------- packages/@aws-cdk/aws-sns/lib/subscriber.ts | 18 +- packages/@aws-cdk/aws-sns/lib/topic-base.ts | 9 +- packages/@aws-cdk/aws-sns/test/test.sns.ts | 44 +++ .../test/integ.ec2-task.expected.json | 40 +- .../test/__snapshots__/synth.test.js.snap | 82 ++--- 19 files changed, 580 insertions(+), 486 deletions(-) diff --git a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json index cf3a99e9c3aab..d344a7f79e42c 100644 --- a/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json +++ b/packages/@aws-cdk/aws-ecs-patterns/test/ec2/integ.scheduled-ecs-task.lit.expected.json @@ -506,34 +506,34 @@ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionA8966A35": { - "Type": "AWS::SNS::Subscription", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegecsEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic0C4958AFBA77E328": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { - "Type": "AWS::Lambda::Permission", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "Endpoint": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, @@ -700,6 +700,10 @@ "Volumes": [] } }, + "ScheduledEc2TaskScheduledTaskDefScheduledContainerLogGroupA85E11E6": { + "Type": "AWS::Logs::LogGroup", + "DeletionPolicy": "Retain" + }, "ScheduledEc2TaskScheduledTaskDefExecutionRole65A8CC6F": { "Type": "AWS::IAM::Role", "Properties": { @@ -826,10 +830,6 @@ ] } }, - "ScheduledEc2TaskScheduledTaskDefScheduledContainerLogGroupA85E11E6": { - "Type": "AWS::Logs::LogGroup", - "DeletionPolicy": "Retain" - }, "ScheduledEc2TaskScheduledEventRuleFE2376A2": { "Type": "AWS::Events::Rule", "Properties": { @@ -868,4 +868,4 @@ "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json index c8269f73154f6..692b586e9ccf0 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-awsvpc-nw.expected.json @@ -1,10 +1,4 @@ { - "Parameters": { - "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" - } - }, "Resources": { "Vpc8378EB38": { "Type": "AWS::EC2::VPC", @@ -516,9 +510,6 @@ } } }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { - "Type": "AWS::SNS::Topic" - }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA": { "Type": "AWS::IAM::Role", "Properties": { @@ -671,34 +662,34 @@ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionA8966A35": { - "Type": "AWS::SNS::Subscription", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic7A89925AFDCBEE50": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { - "Type": "AWS::Lambda::Permission", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "Endpoint": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, @@ -752,6 +743,9 @@ ] } }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { + "Type": "AWS::SNS::Topic" + }, "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029": { "Type": "AWS::AutoScaling::LifecycleHook", "Properties": { @@ -1031,6 +1025,12 @@ } } }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } + }, "Outputs": { "LoadBalancerDNS": { "Value": { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json index 8fcb36c23b453..98e43f40c4912 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.lb-bridge-nw.expected.json @@ -1,10 +1,4 @@ { - "Parameters": { - "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" - } - }, "Resources": { "Vpc8378EB38": { "Type": "AWS::EC2::VPC", @@ -537,9 +531,6 @@ } } }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { - "Type": "AWS::SNS::Topic" - }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA": { "Type": "AWS::IAM::Role", "Properties": { @@ -692,34 +683,34 @@ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionA8966A35": { - "Type": "AWS::SNS::Subscription", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegecsEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic0C4958AFBA77E328": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { - "Type": "AWS::Lambda::Permission", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "Endpoint": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, @@ -773,6 +764,9 @@ ] } }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { + "Type": "AWS::SNS::Topic" + }, "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029": { "Type": "AWS::AutoScaling::LifecycleHook", "Properties": { @@ -994,6 +988,12 @@ } } }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } + }, "Outputs": { "LoadBalancerDNS": { "Value": { diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json index 296b4f57fb608..f341974270a3e 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-awsvpc-nw.expected.json @@ -1,10 +1,4 @@ { - "Parameters": { - "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" - } - }, "Resources": { "Vpc8378EB38": { "Type": "AWS::EC2::VPC", @@ -516,9 +510,6 @@ } } }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { - "Type": "AWS::SNS::Topic" - }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA": { "Type": "AWS::IAM::Role", "Properties": { @@ -671,34 +662,34 @@ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionA8966A35": { - "Type": "AWS::SNS::Subscription", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegecsEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic0C4958AFBA77E328": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { - "Type": "AWS::Lambda::Permission", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "Endpoint": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, @@ -752,6 +743,9 @@ ] } }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { + "Type": "AWS::SNS::Topic" + }, "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029": { "Type": "AWS::AutoScaling::LifecycleHook", "Properties": { @@ -945,5 +939,11 @@ } } } + }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json index 5b9ee07eb04e9..bb423ca150ddd 100644 --- a/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json +++ b/packages/@aws-cdk/aws-ecs/test/ec2/integ.sd-bridge-nw.expected.json @@ -1,10 +1,4 @@ { - "Parameters": { - "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { - "Type": "AWS::SSM::Parameter::Value", - "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" - } - }, "Resources": { "Vpc8378EB38": { "Type": "AWS::EC2::VPC", @@ -516,9 +510,6 @@ } } }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { - "Type": "AWS::SNS::Topic" - }, "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA": { "Type": "AWS::IAM::Role", "Properties": { @@ -671,34 +662,34 @@ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionA8966A35": { - "Type": "AWS::SNS::Subscription", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegecsEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic0C4958AFBA77E328": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { - "Type": "AWS::Lambda::Permission", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "Endpoint": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, @@ -752,6 +743,9 @@ ] } }, + "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4": { + "Type": "AWS::SNS::Topic" + }, "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookFFA63029": { "Type": "AWS::AutoScaling::LifecycleHook", "Properties": { @@ -909,5 +903,11 @@ } } } + }, + "Parameters": { + "SsmParameterValueawsserviceecsoptimizedamiamazonlinux2recommendedimageidC96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id" + } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json index e0f79b8d845ce..7a08a5660234b 100644 --- a/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/codebuild/integ.project-events.expected.json @@ -327,21 +327,6 @@ "MyQueueE6CA6235": { "Type": "AWS::SQS::Queue" }, - "MyTopicMyQueueFA241964": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Protocol": "sqs", - "TopicArn": { - "Ref": "MyTopic86869434" - }, - "Endpoint": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] - } - } - }, "MyQueuePolicy6BBEDDAC": { "Type": "AWS::SQS::QueuePolicy", "Properties": { @@ -377,6 +362,21 @@ ] } }, + "MyQueueawscdkcodebuildeventsMyTopic550011DCF72DE3ED": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + }, "MyTopic86869434": { "Type": "AWS::SNS::Topic" }, diff --git a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json index 3a785ab15d64a..7816a7c459ca5 100644 --- a/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/ecs/integ.event-ec2-task.lit.expected.json @@ -506,34 +506,34 @@ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA" ] }, - "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionA8966A35": { - "Type": "AWS::SNS::Subscription", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsintegecsEcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic0C4958AFBA77E328": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicE6B1EBA6": { - "Type": "AWS::Lambda::Permission", + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" + }, + "Endpoint": { "Fn::GetAtt": [ "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" } } }, @@ -818,6 +818,10 @@ ], "DeletionPolicy": "Delete" }, + "TaskDefTheContainerLogGroupD94C8EF5": { + "Type": "AWS::Logs::LogGroup", + "DeletionPolicy": "Retain" + }, "TaskDefExecutionRoleB4775C97": { "Type": "AWS::IAM::Role", "Properties": { @@ -983,10 +987,6 @@ ] } }, - "TaskDefTheContainerLogGroupD94C8EF5": { - "Type": "AWS::Logs::LogGroup", - "DeletionPolicy": "Retain" - }, "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { "Type": "AWS::IAM::Role", "Properties": { @@ -1197,4 +1197,4 @@ "Description": "Artifact hash for asset \"aws-ecs-integ-ecs/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-events-targets/test/sns/integ.sns-event-rule-target.expected.json b/packages/@aws-cdk/aws-events-targets/test/sns/integ.sns-event-rule-target.expected.json index a6236922383a9..ea44e7de6f50d 100644 --- a/packages/@aws-cdk/aws-events-targets/test/sns/integ.sns-event-rule-target.expected.json +++ b/packages/@aws-cdk/aws-events-targets/test/sns/integ.sns-event-rule-target.expected.json @@ -57,21 +57,6 @@ "MyQueueE6CA6235": { "Type": "AWS::SQS::Queue" }, - "MyTopicMyQueueFA241964": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Protocol": "sqs", - "TopicArn": { - "Ref": "MyTopic86869434" - }, - "Endpoint": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] - } - } - }, "MyQueuePolicy6BBEDDAC": { "Type": "AWS::SQS::QueuePolicy", "Properties": { @@ -106,6 +91,21 @@ } ] } + }, + "MyQueueawscdksnseventtargetMyTopicB7575CD87304D383": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sns.expected.json b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sns.expected.json index 1134471c0d6b7..405bc0a38f133 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sns.expected.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/integ.sns.expected.json @@ -60,34 +60,34 @@ "FServiceRole3AC82EE1" ] }, - "TF2453034D": { - "Type": "AWS::SNS::Subscription", + "FAllowInvokelambdaeventsourcesnsT85539BB11B51A88E": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "TD925BC7E" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "FC4345940", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "TD925BC7E" } } }, - "FT1706F790": { - "Type": "AWS::Lambda::Permission", + "FTA788EE87": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "TD925BC7E" + }, + "Endpoint": { "Fn::GetAtt": [ "FC4345940", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "TD925BC7E" } } }, @@ -95,4 +95,4 @@ "Type": "AWS::SNS::Topic" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts index 7f78adf61b81a..de14680d9338c 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts @@ -24,13 +24,14 @@ export class LambdaSubscription implements sns.ITopicSubscription { throw new Error(`The supplied lambda Function object must be an instance of Construct`); } - this.fn.addPermission(topic.node.id, { + this.fn.addPermission(`AllowInvoke:${topic.node.uniqueId}`, { sourceArn: topic.topicArn, principal: new iam.ServicePrincipal('sns.amazonaws.com'), }); return { - subscriberId: this.fn.node.id, + subscriberScope: this.fn, + subscriberId: topic.node.id, endpoint: this.fn.functionArn, protocol: sns.SubscriptionProtocol.LAMBDA, filterPolicy: this.props.filterPolicy, diff --git a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts index 2fb411f70af34..14f3053c65506 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts @@ -44,7 +44,8 @@ export class SqsSubscription implements sns.ITopicSubscription { })); return { - subscriberId: this.queue.node.id, + subscriberScope: this.queue, + subscriberId: topic.node.uniqueId, endpoint: this.queue.queueArn, protocol: sns.SubscriptionProtocol.SQS, rawMessageDelivery: this.props.rawMessageDelivery, diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda.expected.json b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda.expected.json index c2b5ff331efd6..a49b19b111728 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda.expected.json +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-lambda.expected.json @@ -3,65 +3,6 @@ "MyTopic86869434": { "Type": "AWS::SNS::Topic" }, - "MyTopicEchoD1E0EE5C": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "MyTopic86869434" - }, - "Endpoint": { - "Fn::GetAtt": [ - "Echo11F3FB29", - "Arn" - ] - } - } - }, - "MyTopicFiltered55457D11": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "MyTopic86869434" - }, - "Endpoint": { - "Fn::GetAtt": [ - "Filtered186C0D0A", - "Arn" - ] - }, - "FilterPolicy": { - "color": [ - "red", - { - "prefix": "bl" - }, - { - "prefix": "ye" - } - ], - "size": [ - { - "anything-but": [ - "small", - "medium" - ] - } - ], - "price": [ - { - "numeric": [ - ">=", - 100, - "<=", - 200 - ] - } - ] - } - } - }, "EchoServiceRoleBE28060B": { "Type": "AWS::IAM::Role", "Properties": { @@ -122,7 +63,7 @@ "EchoServiceRoleBE28060B" ] }, - "EchoMyTopicF6EBB45F": { + "EchoAllowInvokeawscdksnslambdaMyTopic6C62AB907F727CDA": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -138,6 +79,21 @@ } } }, + "EchoMyTopic4CB8819E": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "Echo11F3FB29", + "Arn" + ] + } + } + }, "FilteredServiceRole16D9DDC1": { "Type": "AWS::IAM::Role", "Properties": { @@ -198,7 +154,7 @@ "FilteredServiceRole16D9DDC1" ] }, - "FilteredMyTopic804BCBC3": { + "FilteredAllowInvokeawscdksnslambdaMyTopic6C62AB90A2EA1666": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -213,6 +169,50 @@ "Ref": "MyTopic86869434" } } + }, + "FilteredMyTopicC8395C27": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "Filtered186C0D0A", + "Arn" + ] + }, + "FilterPolicy": { + "color": [ + "red", + { + "prefix": "bl" + }, + { + "prefix": "ye" + } + ], + "size": [ + { + "anything-but": [ + "small", + "medium" + ] + } + ], + "price": [ + { + "numeric": [ + ">=", + 100, + "<=", + 200 + ] + } + ] + } + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.expected.json b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.expected.json index 31a0f99f6514a..ab579c50a3541 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.expected.json +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/integ.sns-sqs.lit.expected.json @@ -3,21 +3,6 @@ "MyTopic86869434": { "Type": "AWS::SNS::Topic" }, - "MyTopicMyQueueFA241964": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Protocol": "sqs", - "TopicArn": { - "Ref": "MyTopic86869434" - }, - "Endpoint": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] - } - } - }, "MyQueueE6CA6235": { "Type": "AWS::SQS::Queue" }, @@ -55,6 +40,21 @@ } ] } + }, + "MyQueueawscdksnssqsMyTopic9361DEA223429051": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts index c7189143fdbd2..41c1c180d5824 100644 --- a/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts +++ b/packages/@aws-cdk/aws-sns-subscriptions/test/subs.test.ts @@ -78,67 +78,67 @@ test('queue subscription', () => { expect(stack).toMatchTemplate({ "Resources": { - "MyTopic86869434": { - "Type": "AWS::SNS::Topic", - "Properties": { - "DisplayName": "displayName", - "TopicName": "topicName" - } - }, - "MyTopicMyQueueFA241964": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Endpoint": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] + "MyTopic86869434": { + "Type": "AWS::SNS::Topic", + "Properties": { + "DisplayName": "displayName", + "TopicName": "topicName" + } }, - "Protocol": "sqs", - "TopicArn": { - "Ref": "MyTopic86869434" - } - } - }, - "MyQueueE6CA6235": { - "Type": "AWS::SQS::Queue" - }, - "MyQueuePolicy6BBEDDAC": { - "Type": "AWS::SQS::QueuePolicy", - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": "sqs:SendMessage", - "Condition": { - "ArnEquals": { - "aws:SourceArn": { - "Ref": "MyTopic86869434" - } - } - }, - "Effect": "Allow", - "Principal": { - "Service": "sns.amazonaws.com" + "MyQueueE6CA6235": { + "Type": "AWS::SQS::Queue" + }, + "MyQueuePolicy6BBEDDAC": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "MyTopic86869434" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" }, - "Resource": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" + "Queues": [ + { + "Ref": "MyQueueE6CA6235" + } ] - } } - ], - "Version": "2012-10-17" }, - "Queues": [ - { - "Ref": "MyQueueE6CA6235" + "MyQueueMyTopic9B00631B": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } } - ] } } - } }); }); @@ -173,84 +173,105 @@ test('lambda subscription', () => { expect(stack).toMatchTemplate({ "Resources": { - "MyTopic86869434": { - "Type": "AWS::SNS::Topic", - "Properties": { - "DisplayName": "displayName", - "TopicName": "topicName" - } - }, - "MyTopicMyFunc853BC1D3": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Endpoint": { - "Fn::GetAtt": [ - "MyFunc8A243A2C", - "Arn" - ] - }, - "Protocol": "lambda", - "TopicArn": { - "Ref": "MyTopic86869434" - } - } - }, - "MyFuncServiceRole54065130": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Effect": "Allow", - "Principal": { - "Service": { "Fn::Join": ["", ["lambda.", { Ref: "AWS::URLSuffix" }]] } - } + "MyTopic86869434": { + "Type": "AWS::SNS::Topic", + "Properties": { + "DisplayName": "displayName", + "TopicName": "topicName" } - ], - "Version": "2012-10-17" }, - "ManagedPolicyArns": [ - { "Fn::Join": ["", ["arn:", {"Ref": "AWS::Partition"}, ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]]} - ] - } - }, - "MyFunc8A243A2C": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "exports.handler = function(e, c, cb) { return cb() }" + "MyFuncServiceRole54065130": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "lambda.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } }, - "Handler": "index.handler", - "Role": { - "Fn::GetAtt": [ - "MyFuncServiceRole54065130", - "Arn" + "MyFunc8A243A2C": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(e, c, cb) { return cb() }" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyFuncServiceRole54065130", + "Arn" + ] + }, + "Runtime": "nodejs8.10" + }, + "DependsOn": [ + "MyFuncServiceRole54065130" ] }, - "Runtime": "nodejs8.10" - }, - "DependsOn": [ - "MyFuncServiceRole54065130" - ] - }, - "MyFuncMyTopicC77D8FAB": { - "Type": "AWS::Lambda::Permission", - "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { - "Fn::GetAtt": [ - "MyFunc8A243A2C", - "Arn" - ] + "MyFuncAllowInvokeMyTopicDD0A15B8": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "MyFunc8A243A2C", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "MyTopic86869434" + } + } }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "MyTopic86869434" - } + "MyFuncMyTopic93B6FB00": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyFunc8A243A2C", + "Arn" + ] + } + } } } - } }); }); @@ -300,36 +321,6 @@ test('multiple subscriptions', () => { "TopicName": "topicName" } }, - "MyTopicMyQueueFA241964": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Endpoint": { - "Fn::GetAtt": [ - "MyQueueE6CA6235", - "Arn" - ] - }, - "Protocol": "sqs", - "TopicArn": { - "Ref": "MyTopic86869434" - } - } - }, - "MyTopicMyFunc853BC1D3": { - "Type": "AWS::SNS::Subscription", - "Properties": { - "Endpoint": { - "Fn::GetAtt": [ - "MyFunc8A243A2C", - "Arn" - ] - }, - "Protocol": "lambda", - "TopicArn": { - "Ref": "MyTopic86869434" - } - } - }, "MyQueueE6CA6235": { "Type": "AWS::SQS::Queue" }, @@ -368,6 +359,21 @@ test('multiple subscriptions', () => { ] } }, + "MyQueueMyTopic9B00631B": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "sqs", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyQueueE6CA6235", + "Arn" + ] + } + } + }, "MyFuncServiceRole54065130": { "Type": "AWS::IAM::Role", "Properties": { @@ -377,7 +383,17 @@ test('multiple subscriptions', () => { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { - "Service": { "Fn::Join": ["", ["lambda.", { Ref: "AWS::URLSuffix" }]] } + "Service": { + "Fn::Join": [ + "", + [ + "lambda.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } } } ], @@ -418,7 +434,7 @@ test('multiple subscriptions', () => { "MyFuncServiceRole54065130" ] }, - "MyFuncMyTopicC77D8FAB": { + "MyFuncAllowInvokeMyTopicDD0A15B8": { "Type": "AWS::Lambda::Permission", "Properties": { "Action": "lambda:InvokeFunction", @@ -433,6 +449,21 @@ test('multiple subscriptions', () => { "Ref": "MyTopic86869434" } } + }, + "MyFuncMyTopic93B6FB00": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "MyTopic86869434" + }, + "Endpoint": { + "Fn::GetAtt": [ + "MyFunc8A243A2C", + "Arn" + ] + } + } } } }); @@ -444,7 +475,7 @@ test('throws with mutliple subscriptions of the same subscriber', () => { topic.addSubscription(new subs.SqsSubscription(queue)); expect(() => topic.addSubscription(new subs.SqsSubscription(queue))) - .toThrowError(/subscriber MyQueue already exists/); + .toThrowError(/A subscription with id \"MyTopic\" already exists under the scope MyQueue/); }); test('with filter policy', () => { diff --git a/packages/@aws-cdk/aws-sns/lib/subscriber.ts b/packages/@aws-cdk/aws-sns/lib/subscriber.ts index 1077732ef8df9..33e9a3506e449 100644 --- a/packages/@aws-cdk/aws-sns/lib/subscriber.ts +++ b/packages/@aws-cdk/aws-sns/lib/subscriber.ts @@ -1,3 +1,4 @@ +import { Construct } from '@aws-cdk/core'; import { SubscriptionOptions } from './subscription'; import { ITopic } from './topic-base'; @@ -6,8 +7,21 @@ import { ITopic } from './topic-base'; */ export interface TopicSubscriptionConfig extends SubscriptionOptions { /** - * The id of the subscriber. Will be used as the id for the subscription in - * the topic's scope. + * The scope in which to create the SNS subscription resource. Normally you'd + * want the subscription to be created on the consuming stack because the + * topic is usually referenced by the consumer's resource policy (e.g. SQS + * queue policy). Otherwise, it will cause a cyclic reference. + * + * If this is undefined, the subscription will be created on the topic's stack. + * + * @default - use the topic as the scope of the subscription, in which case `subscriberId` must be defined. + */ + readonly subscriberScope?: Construct; + + /** + * The id of the SNS subscription resource created under `scope`. In most + * cases, it is recommended to use the `uniqueId` of the topic you are + * subscribing to. */ readonly subscriberId: string; } diff --git a/packages/@aws-cdk/aws-sns/lib/topic-base.ts b/packages/@aws-cdk/aws-sns/lib/topic-base.ts index 35bf01907d0c8..0f532da3d1afb 100644 --- a/packages/@aws-cdk/aws-sns/lib/topic-base.ts +++ b/packages/@aws-cdk/aws-sns/lib/topic-base.ts @@ -58,13 +58,16 @@ export abstract class TopicBase extends Resource implements ITopic { public addSubscription(subscription: ITopicSubscription) { const subscriptionConfig = subscription.bind(this); + const scope = subscriptionConfig.subscriberScope || this; + const id = subscriptionConfig.subscriberId; + // We use the subscriber's id as the construct id. There's no meaning // to subscribing the same subscriber twice on the same topic. - if (this.node.tryFindChild(subscriptionConfig.subscriberId)) { - throw new Error(`A subscription between the topic ${this.node.id} and the subscriber ${subscriptionConfig.subscriberId} already exists`); + if (scope.node.tryFindChild(id)) { + throw new Error(`A subscription with id "${id}" already exists under the scope ${scope.node.path}`); } - new Subscription(this, subscriptionConfig.subscriberId, { + new Subscription(scope, id, { topic: this, ...subscriptionConfig, }); diff --git a/packages/@aws-cdk/aws-sns/test/test.sns.ts b/packages/@aws-cdk/aws-sns/test/test.sns.ts index 53a5c9e68d34e..2057dd3d850bd 100644 --- a/packages/@aws-cdk/aws-sns/test/test.sns.ts +++ b/packages/@aws-cdk/aws-sns/test/test.sns.ts @@ -1,8 +1,10 @@ import { expect, haveResource } from '@aws-cdk/assert'; import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/core'); +import { App, Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import sns = require('../lib'); +import { SubscriptionProtocol, Topic } from '../lib'; // tslint:disable:object-literal-key-quotes @@ -223,6 +225,48 @@ export = { statistic: 'Average' }); + test.done(); + }, + + 'subscription is created under the topic scope by default'(test: Test) { + // GIVEN + const stack = new Stack(); + const topic = new Topic(stack, 'Topic'); + + // WHEN + topic.addSubscription({ + bind: () => ({ + protocol: SubscriptionProtocol.HTTP, + endpoint: 'http://foo/bar', + subscriberId: 'my-subscription' + }) + }); + + // THEN + expect(stack).to(haveResource('AWS::SNS::Subscription')); + test.done(); + }, + + 'if "scope" is defined, subscription will be created under that scope'(test: Test) { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'A'); + const stack2 = new Stack(app, 'B'); + const topic = new Topic(stack, 'Topic'); + + // WHEN + topic.addSubscription({ + bind: () => ({ + protocol: SubscriptionProtocol.HTTP, + endpoint: 'http://foo/bar', + subscriberScope: stack2, + subscriberId: 'subscriberId' + }) + }); + + // THEN + expect(stack).notTo(haveResource('AWS::SNS::Subscription')); + expect(stack2).to(haveResource('AWS::SNS::Subscription')); test.done(); } }; diff --git a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json index db8e2e9338110..4d03787417868 100644 --- a/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json +++ b/packages/@aws-cdk/aws-stepfunctions-tasks/test/integ.ec2-task.expected.json @@ -306,34 +306,34 @@ "FargateClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole7FEDCD32" ] }, - "FargateClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicFunctionE74B772A": { - "Type": "AWS::SNS::Subscription", + "FargateClusterDefaultAutoScalingGroupDrainECSHookFunctionAllowInvokeawsecsinteg2FargateClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic9C6EC468C75B1F21": { + "Type": "AWS::Lambda::Permission", "Properties": { - "Protocol": "lambda", - "TopicArn": { - "Ref": "FargateClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic49146C10" - }, - "Endpoint": { + "Action": "lambda:InvokeFunction", + "FunctionName": { "Fn::GetAtt": [ "FargateClusterDefaultAutoScalingGroupDrainECSHookFunctionE3D5BEE8", "Arn" ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "FargateClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic49146C10" } } }, - "FargateClusterDefaultAutoScalingGroupDrainECSHookFunctionTopicA1F1F9E9": { - "Type": "AWS::Lambda::Permission", + "FargateClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic37856E82": { + "Type": "AWS::SNS::Subscription", "Properties": { - "Action": "lambda:InvokeFunction", - "FunctionName": { + "Protocol": "lambda", + "TopicArn": { + "Ref": "FargateClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic49146C10" + }, + "Endpoint": { "Fn::GetAtt": [ "FargateClusterDefaultAutoScalingGroupDrainECSHookFunctionE3D5BEE8", "Arn" ] - }, - "Principal": "sns.amazonaws.com", - "SourceArn": { - "Ref": "FargateClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopic49146C10" } } }, @@ -600,6 +600,10 @@ ], "DeletionPolicy": "Delete" }, + "TaskDefTheContainerLogGroupD94C8EF5": { + "Type": "AWS::Logs::LogGroup", + "DeletionPolicy": "Retain" + }, "TaskDefExecutionRoleB4775C97": { "Type": "AWS::IAM::Role", "Properties": { @@ -687,10 +691,6 @@ ] } }, - "TaskDefTheContainerLogGroupD94C8EF5": { - "Type": "AWS::Logs::LogGroup", - "DeletionPolicy": "Retain" - }, "AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62cServiceRoleD788AA17": { "Type": "AWS::IAM::Role", "Properties": { @@ -978,4 +978,4 @@ "Description": "Artifact hash for asset \"aws-ecs-integ2/AdoptEcrRepositorydbc60defc59544bcaa5c28c95d68f62c/Code\"" } } -} +} \ No newline at end of file diff --git a/packages/decdk/test/__snapshots__/synth.test.js.snap b/packages/decdk/test/__snapshots__/synth.test.js.snap index 31b9df6d66d08..fefb681ed9aee 100644 --- a/packages/decdk/test/__snapshots__/synth.test.js.snap +++ b/packages/decdk/test/__snapshots__/synth.test.js.snap @@ -979,6 +979,22 @@ Object { }, }, "Resources": Object { + "HelloWorldFunctionAllowInvokelambdaeventsMyTopic988FAB3E43035740": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Fn::GetAtt": Array [ + "HelloWorldFunctionB2AB6E79", + "Arn", + ], + }, + "Principal": "sns.amazonaws.com", + "SourceArn": Object { + "Ref": "MyTopic86869434", + }, + }, + "Type": "AWS::Lambda::Permission", + }, "HelloWorldFunctionApiPermissionGEThello0F001828": Object { "Properties": Object { "Action": "lambda:InvokeFunction", @@ -1211,21 +1227,20 @@ Object { }, "Type": "AWS::Lambda::EventSourceMapping", }, - "HelloWorldFunctionMyTopic7834422D": Object { + "HelloWorldFunctionMyTopic90F6E0EF": Object { "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { + "Endpoint": Object { "Fn::GetAtt": Array [ "HelloWorldFunctionB2AB6E79", "Arn", ], }, - "Principal": "sns.amazonaws.com", - "SourceArn": Object { + "Protocol": "lambda", + "TopicArn": Object { "Ref": "MyTopic86869434", }, }, - "Type": "AWS::Lambda::Permission", + "Type": "AWS::SNS::Subscription", }, "HelloWorldFunctionServiceRole8E0BD458": Object { "Properties": Object { @@ -1306,21 +1321,6 @@ Object { "MyTopic86869434": Object { "Type": "AWS::SNS::Topic", }, - "MyTopicHelloWorldFunction831B106E": Object { - "Properties": Object { - "Endpoint": Object { - "Fn::GetAtt": Array [ - "HelloWorldFunctionB2AB6E79", - "Arn", - ], - }, - "Protocol": "lambda", - "TopicArn": Object { - "Ref": "MyTopic86869434", - }, - }, - "Type": "AWS::SNS::Subscription", - }, "TableCD117FA1": Object { "DeletionPolicy": "Retain", "Properties": Object { @@ -1549,6 +1549,22 @@ Object { }, }, "Resources": Object { + "LambdaAllowInvokelambdatopicTopic702108A3CE1309A3": Object { + "Properties": Object { + "Action": "lambda:InvokeFunction", + "FunctionName": Object { + "Fn::GetAtt": Array [ + "LambdaD247545B", + "Arn", + ], + }, + "Principal": "sns.amazonaws.com", + "SourceArn": Object { + "Ref": "TopicBFC7AF6E", + }, + }, + "Type": "AWS::Lambda::Permission", + }, "LambdaD247545B": Object { "DependsOn": Array [ "LambdaServiceRoleA8ED4D3B", @@ -1644,26 +1660,7 @@ Object { }, "Type": "AWS::IAM::Role", }, - "LambdaTopic59002359": Object { - "Properties": Object { - "Action": "lambda:InvokeFunction", - "FunctionName": Object { - "Fn::GetAtt": Array [ - "LambdaD247545B", - "Arn", - ], - }, - "Principal": "sns.amazonaws.com", - "SourceArn": Object { - "Ref": "TopicBFC7AF6E", - }, - }, - "Type": "AWS::Lambda::Permission", - }, - "TopicBFC7AF6E": Object { - "Type": "AWS::SNS::Topic", - }, - "TopicLambda3DD31D45": Object { + "LambdaTopicCCB0C2A3": Object { "Properties": Object { "Endpoint": Object { "Fn::GetAtt": Array [ @@ -1678,6 +1675,9 @@ Object { }, "Type": "AWS::SNS::Subscription", }, + "TopicBFC7AF6E": Object { + "Type": "AWS::SNS::Topic", + }, }, } `;