Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sns-subscriptions): enable cross region subscriptions to sqs and lambda #17273

Merged
merged 3 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion packages/@aws-cdk/aws-sns-subscriptions/lib/lambda.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import * as sns from '@aws-cdk/aws-sns';
import { Names, Stack } from '@aws-cdk/core';
import { Names, Stack, Token } from '@aws-cdk/core';
import { SubscriptionProps } from './subscription';

// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
Expand Down Expand Up @@ -36,6 +36,12 @@ export class LambdaSubscription implements sns.ITopicSubscription {
principal: new iam.ServicePrincipal('sns.amazonaws.com'),
});

// if the topic and function are created in different stacks
// then we need to make sure the topic is created first
if (topic instanceof sns.Topic && topic.stack !== this.fn.stack) {
this.fn.stack.addDependency(topic.stack);
}

return {
subscriberScope: this.fn,
subscriberId: topic.node.id,
Expand All @@ -50,6 +56,14 @@ export class LambdaSubscription implements sns.ITopicSubscription {
private regionFromArn(topic: sns.ITopic): string | undefined {
// no need to specify `region` for topics defined within the same stack.
if (topic instanceof sns.Topic) {
if (topic.stack !== this.fn.stack) {
// only if we know the region, will not work for
// env agnostic stacks
if (!Token.isUnresolved(topic.stack.region) &&
(topic.stack.region !== this.fn.stack.region)) {
return topic.stack.region;
}
}
return undefined;
}
return Stack.of(topic).parseArn(topic.topicArn).region;
Expand Down
16 changes: 15 additions & 1 deletion packages/@aws-cdk/aws-sns-subscriptions/lib/sqs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as iam from '@aws-cdk/aws-iam';
import * as sns from '@aws-cdk/aws-sns';
import * as sqs from '@aws-cdk/aws-sqs';
import { Names, Stack } from '@aws-cdk/core';
import { Names, Stack, Token } from '@aws-cdk/core';
import { SubscriptionProps } from './subscription';

// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
Expand Down Expand Up @@ -61,6 +61,12 @@ export class SqsSubscription implements sns.ITopicSubscription {
}));
}

// if the topic and queue are created in different stacks
// then we need to make sure the topic is created first
if (topic instanceof sns.Topic && topic.stack !== this.queue.stack) {
this.queue.stack.addDependency(topic.stack);
}

return {
subscriberScope: this.queue,
subscriberId: Names.nodeUniqueId(topic.node),
Expand All @@ -76,6 +82,14 @@ export class SqsSubscription implements sns.ITopicSubscription {
private regionFromArn(topic: sns.ITopic): string | undefined {
// no need to specify `region` for topics defined within the same stack
if (topic instanceof sns.Topic) {
if (topic.stack !== this.queue.stack) {
// only if we know the region, will not work for
// env agnostic stacks
if (!Token.isUnresolved(topic.stack.region) &&
(topic.stack.region !== this.queue.stack.region)) {
return topic.stack.region;
}
}
return undefined;
}
return Stack.of(topic).parseArn(topic.topicArn).region;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
[
{
"Resources": {
"MyTopic86869434": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "topicstackopicstackmytopicc43e67afb24f28bb94f9"
}
}
}
},
{
"Resources": {
"EchoServiceRoleBE28060B": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
]
]
}
]
}
},
"Echo11F3FB29": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"ZipFile": "exports.handler = function handler(event, _context, callback) {\n /* eslint-disable no-console */\n console.log('====================================================');\n console.log(JSON.stringify(event, undefined, 2));\n console.log('====================================================');\n return callback(undefined, event);\n}"
},
"Role": {
"Fn::GetAtt": [
"EchoServiceRoleBE28060B",
"Arn"
]
},
"Handler": "index.handler",
"Runtime": "nodejs10.x"
},
"DependsOn": [
"EchoServiceRoleBE28060B"
]
},
"EchoAllowInvokeTopicStackMyTopicC43E67AF32CF6EFA": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"Echo11F3FB29",
"Arn"
]
},
"Principal": "sns.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9"
]
]
}
}
},
"EchoMyTopic4CB8819E": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"Protocol": "lambda",
"TopicArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9"
]
]
},
"Endpoint": {
"Fn::GetAtt": [
"Echo11F3FB29",
"Arn"
]
},
"Region": "us-east-1"
}
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as lambda from '@aws-cdk/aws-lambda';
import * as sns from '@aws-cdk/aws-sns';
import * as cdk from '@aws-cdk/core';
import * as subs from '../lib';

/// !cdk-integ *
const app = new cdk.App();

const topicStack = new cdk.Stack(app, 'TopicStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' },
});
const topic = new sns.Topic(topicStack, 'MyTopic', {
topicName: cdk.PhysicalName.GENERATE_IF_NEEDED,
});

const functionStack = new cdk.Stack(app, 'FunctionStack', {
env: { region: 'us-east-2' },
});
const fction = new lambda.Function(functionStack, 'Echo', {
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_10_X,
code: lambda.Code.fromInline(`exports.handler = ${handler.toString()}`),
});

topic.addSubscription(new subs.LambdaSubscription(fction));

app.synth();

function handler(event: any, _context: any, callback: any) {
/* eslint-disable no-console */
console.log('====================================================');
console.log(JSON.stringify(event, undefined, 2));
console.log('====================================================');
return callback(undefined, event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
[
{
"Resources": {
"MyTopic86869434": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "topicstackopicstackmytopicc43e67afb24f28bb94f9"
}
}
}
},
{
"Resources": {
"MyQueueE6CA6235": {
"Type": "AWS::SQS::Queue",
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
},
"MyQueuePolicy6BBEDDAC": {
"Type": "AWS::SQS::QueuePolicy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "sqs:SendMessage",
"Condition": {
"ArnEquals": {
"aws:SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9"
]
]
}
}
},
"Effect": "Allow",
"Principal": {
"Service": "sns.amazonaws.com"
},
"Resource": {
"Fn::GetAtt": [
"MyQueueE6CA6235",
"Arn"
]
}
}
],
"Version": "2012-10-17"
},
"Queues": [
{
"Ref": "MyQueueE6CA6235"
}
]
}
},
"MyQueueTopicStackMyTopicC43E67AFC8DC8B4A": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"Protocol": "sqs",
"TopicArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":sns:us-east-1:12345678:topicstackopicstackmytopicc43e67afb24f28bb94f9"
]
]
},
"Endpoint": {
"Fn::GetAtt": [
"MyQueueE6CA6235",
"Arn"
]
},
"Region": "us-east-1"
}
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as sns from '@aws-cdk/aws-sns';
import * as sqs from '@aws-cdk/aws-sqs';
import * as cdk from '@aws-cdk/core';
import * as subs from '../lib';

/// !cdk-integ *
const app = new cdk.App();

/// !show
const topicStack = new cdk.Stack(app, 'TopicStack', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' },
});
const topic = new sns.Topic(topicStack, 'MyTopic', {
topicName: cdk.PhysicalName.GENERATE_IF_NEEDED,
});

const queueStack = new cdk.Stack(app, 'QueueStack', {
env: { region: 'us-east-2' },
});
const queue = new sqs.Queue(queueStack, 'MyQueue');

topic.addSubscription(new subs.SqsSubscription(queue));
/// !hide

app.synth();
Loading