Skip to content

Commit 7fe55a1

Browse files
authored
refactor ref keys in conditional mappings (#11693)
1 parent a273e83 commit 7fe55a1

File tree

5 files changed

+86
-62
lines changed

5 files changed

+86
-62
lines changed

localstack-core/localstack/services/cloudformation/engine/template_utils.py

Lines changed: 24 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,24 @@ def resolve_pseudo_parameter(
143143
return AWS_URL_SUFFIX
144144

145145

146+
def resolve_conditional_mapping_ref(
147+
ref_name, account_id: str, region_name: str, stack_name: str, parameters
148+
):
149+
if ref_name.startswith("AWS::"):
150+
ref_value = resolve_pseudo_parameter(account_id, region_name, ref_name, stack_name)
151+
if ref_value is None:
152+
raise TemplateError(f"Invalid pseudo parameter '{ref_name}'")
153+
else:
154+
param = parameters.get(ref_name)
155+
if not param:
156+
raise TemplateError(
157+
f"Invalid reference: '{ref_name}' does not exist in parameters: '{parameters}'"
158+
)
159+
ref_value = param.get("ResolvedValue") or param.get("ParameterValue")
160+
161+
return ref_value
162+
163+
146164
def resolve_condition(
147165
account_id: str, region_name: str, condition, conditions, parameters, mappings, stack_name
148166
):
@@ -184,49 +202,20 @@ def resolve_condition(
184202
map_name, top_level_key, second_level_key = v
185203
if isinstance(map_name, dict) and "Ref" in map_name:
186204
ref_name = map_name["Ref"]
187-
param = parameters.get(ref_name) or resolve_pseudo_parameter(
188-
account_id, region_name, ref_name, stack_name
189-
)
190-
if not param:
191-
raise TemplateError(
192-
f"Invalid reference: '{ref_name}' does not exist in parameters: '{parameters}'"
193-
)
194-
map_name = (
195-
(param.get("ResolvedValue") or param.get("ParameterValue"))
196-
if isinstance(param, dict)
197-
else param
205+
map_name = resolve_conditional_mapping_ref(
206+
ref_name, account_id, region_name, stack_name, parameters
198207
)
199208

200209
if isinstance(top_level_key, dict) and "Ref" in top_level_key:
201210
ref_name = top_level_key["Ref"]
202-
param = parameters.get(ref_name) or resolve_pseudo_parameter(
203-
account_id, region_name, ref_name, stack_name
204-
)
205-
if not param:
206-
raise TemplateError(
207-
f"Invalid reference: '{ref_name}' does not exist in parameters: '{parameters}'"
208-
)
209-
top_level_key = (
210-
(param.get("ResolvedValue") or param.get("ParameterValue"))
211-
if isinstance(param, dict)
212-
else param
211+
top_level_key = resolve_conditional_mapping_ref(
212+
ref_name, account_id, region_name, stack_name, parameters
213213
)
214214

215215
if isinstance(second_level_key, dict) and "Ref" in second_level_key:
216216
ref_name = second_level_key["Ref"]
217-
param = parameters.get(ref_name) or resolve_pseudo_parameter(
218-
account_id, region_name, ref_name, stack_name
219-
)
220-
if not param:
221-
raise TemplateError(
222-
f"Invalid reference: '{ref_name}' does not exist in parameters: '{parameters}'"
223-
)
224-
225-
# TODO add validation, second level cannot contain symbols
226-
second_level_key = (
227-
(param.get("ResolvedValue") or param.get("ParameterValue"))
228-
if isinstance(param, dict)
229-
else param
217+
second_level_key = resolve_conditional_mapping_ref(
218+
ref_name, account_id, region_name, stack_name, parameters
230219
)
231220

232221
mapping = mappings.get(map_name)

tests/aws/services/cloudformation/engine/test_mappings.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,20 @@ def test_mapping_ref_map_key(self, deploy_cfn_template, aws_client, map_key, sho
183183
assert topic_arn is not None
184184

185185
aws_client.sns.get_topic_attributes(TopicArn=topic_arn)
186+
187+
@markers.aws.validated
188+
def test_aws_refs_in_mappings(self, deploy_cfn_template, account_id):
189+
"""
190+
This test asserts that Pseudo references aka "AWS::" are supported inside a mapping inside a Conditional.
191+
It's worth remembering that even with references being supported, AWS rejects names that are not alphanumeric
192+
in Mapping name or the second level key.
193+
"""
194+
stack_name = f"Stack{short_uid()}"
195+
stack = deploy_cfn_template(
196+
template_path=os.path.join(
197+
THIS_DIR, "../../../templates/mappings/mapping-aws-ref-map-key.yaml"
198+
),
199+
stack_name=stack_name,
200+
template_mapping={"StackName": stack_name},
201+
)
202+
assert stack.outputs.get("TopicArn")

tests/aws/services/cloudformation/engine/test_mappings.validation.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
{
2+
"tests/aws/services/cloudformation/engine/test_mappings.py::TestCloudFormationMappings::test_aws_refs_in_mappings": {
3+
"last_validated_date": "2024-10-15T17:22:43+00:00"
4+
},
25
"tests/aws/services/cloudformation/engine/test_mappings.py::TestCloudFormationMappings::test_mapping_maximum_nesting_depth": {
36
"last_validated_date": "2023-06-12T14:47:24+00:00"
47
},
58
"tests/aws/services/cloudformation/engine/test_mappings.py::TestCloudFormationMappings::test_mapping_minimum_nesting_depth": {
69
"last_validated_date": "2023-06-12T14:47:25+00:00"
710
},
811
"tests/aws/services/cloudformation/engine/test_mappings.py::TestCloudFormationMappings::test_mapping_ref_map_key[should-deploy]": {
9-
"last_validated_date": "2024-10-10T20:08:36+00:00"
12+
"last_validated_date": "2024-10-17T22:40:44+00:00"
1013
},
1114
"tests/aws/services/cloudformation/engine/test_mappings.py::TestCloudFormationMappings::test_mapping_ref_map_key[should-not-deploy]": {
12-
"last_validated_date": "2024-10-10T20:09:34+00:00"
15+
"last_validated_date": "2024-10-17T22:41:45+00:00"
1316
},
1417
"tests/aws/services/cloudformation/engine/test_mappings.py::TestCloudFormationMappings::test_mapping_with_invalid_refs": {
1518
"last_validated_date": "2023-06-12T14:47:24+00:00"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Mappings:
2+
{{StackName}}:
3+
us-east-1:
4+
{{StackName}}: "true"
5+
us-east-2:
6+
{{StackName}}: "true"
7+
us-west-1:
8+
{{StackName}}: "true"
9+
us-west-2:
10+
{{StackName}}: "true"
11+
ap-southeast-2:
12+
{{StackName}}: "true"
13+
ap-northeast-1:
14+
{{StackName}}: "true"
15+
eu-central-1:
16+
{{StackName}}: "true"
17+
eu-west-1:
18+
{{StackName}}: "true"
19+
20+
21+
Conditions:
22+
MyCondition: !Equals
23+
- !FindInMap [ !Ref AWS::StackName, !Ref AWS::Region, !Ref AWS::StackName ]
24+
- "true"
25+
26+
Resources:
27+
MyTopic:
28+
Type: AWS::SNS::Topic
29+
Condition: MyCondition
30+
31+
32+
Outputs:
33+
TopicArn:
34+
Value: !Ref MyTopic
35+

tests/aws/templates/mappings/mapping-ref-map-key.yaml

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,13 @@
11
Mappings:
22
MyMap:
3-
us-east-1:
4-
A: "true"
5-
B: "false"
6-
us-east-2:
7-
A: "true"
8-
B: "false"
9-
us-west-1:
10-
A: "true"
11-
B: "false"
12-
us-west-2:
13-
A: "true"
14-
B: "false"
15-
ap-southeast-2:
16-
A: "true"
17-
B: "false"
18-
ap-northeast-1:
19-
A: "true"
20-
B: "false"
21-
eu-central-1:
22-
A: "true"
23-
B: "false"
24-
eu-west-1:
25-
A: "true"
26-
B: "false"
3+
A:
4+
value: "true"
5+
B:
6+
value: "false"
277

288
Conditions:
299
MyCondition: !Equals
30-
- !FindInMap [ !Ref MapName, !Ref AWS::Region, !Ref MapKey]
10+
- !FindInMap [ !Ref MapName, !Ref MapKey, value ]
3111
- "true"
3212

3313
Parameters:

0 commit comments

Comments
 (0)