Skip to content

Commit

Permalink
Support subscribing to SNS ARNs
Browse files Browse the repository at this point in the history
This updates `@app.on_sns_message` to accept either the topic name or
the topic ARN rather than just the name. While accepting the name can
be a simpler experience, it prevents users from subscribing to topics
that are in other regions or other accounts.
  • Loading branch information
JordonPhillips committed Jan 15, 2019
1 parent 8426d4c commit 03b6d58
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Next Release (TBD)

* Fix packaging multiple local directories as dependencies
(`#1047 <https://github.com/aws/chalice/pull/1047>`__)
* Add support for passing SNS ARNs to ``on_sns_message``
(`#1048 <https://github.com/aws/chalice/pull/1048>`__)


1.6.2
Expand Down
56 changes: 33 additions & 23 deletions chalice/deploy/planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,31 +439,41 @@ def _plan_snslambdasubscription(self, resource):
)
topic_arn_varname = '%s_topic_arn' % resource.resource_name
subscribe_varname = '%s_subscription_arn' % resource.resource_name
# To keep the user API simple, we only require the topic
# name and not the ARN. However, the APIs require the topic
# ARN so we need to reconstruct it here in the planner.
instruction_for_topic_arn = [
models.BuiltinFunction(
'parse_arn',
[function_arn],
output_var='parsed_lambda_arn',
),
models.JPSearch('account_id',
input_var='parsed_lambda_arn',
output_var='account_id'),
models.JPSearch('region',
input_var='parsed_lambda_arn',
output_var='region_name'),
models.StoreValue(
name=topic_arn_varname,
value=StringFormat(
'arn:aws:sns:{region_name}:{account_id}:%s' % (
resource.topic

instruction_for_topic_arn = [] # type: List[InstructionMsg]
if resource.topic.startswith('arn:aws:sns:'):
instruction_for_topic_arn += [
models.StoreValue(
name=topic_arn_varname,
value=resource.topic,
)
]
else:
# To keep the user API simple, we only require the topic
# name and not the ARN. However, the APIs require the topic
# ARN so we need to reconstruct it here in the planner.
instruction_for_topic_arn += [
models.BuiltinFunction(
'parse_arn',
[function_arn],
output_var='parsed_lambda_arn',
),
models.JPSearch('account_id',
input_var='parsed_lambda_arn',
output_var='account_id'),
models.JPSearch('region',
input_var='parsed_lambda_arn',
output_var='region_name'),
models.StoreValue(
name=topic_arn_varname,
value=StringFormat(
'arn:aws:sns:{region_name}:{account_id}:%s' % (
resource.topic
),
['region_name', 'account_id'],
),
['region_name', 'account_id'],
),
),
] # type: List[InstructionMsg]
]
if self._remote_state.resource_exists(resource):
# Given there's nothing about an SNS subscription you can
# configure for now, if the resource exists, we don't do
Expand Down
3 changes: 1 addition & 2 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ Chalice
app.log.info("SNS subject: %s", event.subject)
app.log.info("SNS message: %s", event.message)
:param topic: The name of the SNS topic you want to subscribe to.
This is the name of the topic, not the topic ARN.
:param topic: The name or ARN of the SNS topic you want to subscribe to.

:param name: The name of the function to use. This name is combined
with the chalice app name as well as the stage name to create the
Expand Down
4 changes: 4 additions & 0 deletions docs/source/topics/events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ command::
2018-06-28 17:49:30.513000 547e0f chalice-demo-sns - DEBUG - Received message with subject: TestSubject1, message: TestMessage1
2018-06-28 17:49:40.391000 547e0f chalice-demo-sns - DEBUG - Received message with subject: TestSubject2, message: TestMessage2

In this example we used the SNS topic name to register our handler, but you can
also use the topic arn. This can be useful if your topic is in another region
or account.


.. _sqs-events:

Expand Down
57 changes: 57 additions & 0 deletions tests/unit/deploy/test_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,63 @@ def test_can_plan_sns_subscription(self):
),
]

def test_can_plan_sns_arn_subscription(self):
function = create_function_resource('function_name')
topic_arn = 'arn:aws:sns:mars-west-2:123456789:mytopic'
sns_subscription = models.SNSLambdaSubscription(
resource_name='function_name-sns-subscription',
topic=topic_arn,
lambda_function=function
)
plan = self.determine_plan(sns_subscription)
plan_parse_arn = plan[0]
assert plan_parse_arn == models.StoreValue(
name='function_name-sns-subscription_topic_arn',
value=topic_arn,
)
topic_arn_var = Variable("function_name-sns-subscription_topic_arn")
assert plan[1:] == [
models.APICall(
method_name='add_permission_for_sns_topic',
params={
'function_arn': Variable("function_name_lambda_arn"),
'topic_arn': topic_arn_var,
},
output_var=None
),
models.APICall(
method_name='subscribe_function_to_topic',
params={
'function_arn': Variable("function_name_lambda_arn"),
'topic_arn': topic_arn_var,
},
output_var='function_name-sns-subscription_subscription_arn'
),
models.RecordResourceValue(
resource_type='sns_event',
resource_name='function_name-sns-subscription',
name='topic',
value=topic_arn),
models.RecordResourceVariable(
resource_type='sns_event',
resource_name='function_name-sns-subscription',
name='lambda_arn',
variable_name='function_name_lambda_arn'
),
models.RecordResourceVariable(
resource_type='sns_event',
resource_name='function_name-sns-subscription',
name='subscription_arn',
variable_name='function_name-sns-subscription_subscription_arn'
),
models.RecordResourceVariable(
resource_type='sns_event',
resource_name='function_name-sns-subscription',
name='topic_arn',
variable_name='function_name-sns-subscription_topic_arn',
),
]

def test_sns_subscription_exists_is_noop_for_planner(self):
function = create_function_resource('function_name')
sns_subscription = models.SNSLambdaSubscription(
Expand Down

0 comments on commit 03b6d58

Please sign in to comment.