Skip to content

Commit 10338bd

Browse files
authored
Handle AWS list functions in template generator (#2331)
The template generator would introduce a list wrapping for AWS Functions (Cidr, GetAZs, Split) that return a list resulting in the function results wrapped in a list. This detects and corrects that behavior.
1 parent cd9b4b1 commit 10338bd

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

tests/test_template_generator.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
import unittest
33

4-
from troposphere import AWSObject, Template
4+
from troposphere import AWSObject, Cidr, GetAZs, Split, Template
55
from troposphere.template_generator import (
66
ResourceTypeNotDefined,
77
ResourceTypeNotFound,
@@ -117,6 +117,34 @@ def test_no_nested_name(self):
117117
name = d["Outputs"]["TestOutput"]["Export"]["Name"]
118118
self.assertIn("Fn::Sub", name)
119119

120+
def test_list_functions(self):
121+
"""
122+
Ensures that list functions are handled properly..
123+
"""
124+
template = Template()
125+
template.add_resource(
126+
MyListResource(
127+
"foo",
128+
AZs=GetAZs(""),
129+
Cidr=Cidr("192.168.0.0/24", "6", "5"),
130+
Split=Split(",", "a,b,c"),
131+
)
132+
)
133+
generated = TemplateGenerator(
134+
json.loads(template.to_json()), CustomMembers=[MyListResource]
135+
)
136+
137+
# validated that the templates are equal to each other
138+
self.assertDictEqual(template.to_dict(), generated.to_dict())
139+
140+
# Further validate the list types are correct
141+
generated_azs = generated.resources["foo"].properties["AZs"]
142+
assert isinstance(generated_azs, GetAZs)
143+
generated_cidr = generated.resources["foo"].properties["Cidr"]
144+
assert isinstance(generated_cidr, Cidr)
145+
generated_split = generated.resources["foo"].properties["Split"]
146+
assert isinstance(generated_split, Split)
147+
120148

121149
class MyCustomResource(AWSObject):
122150
resource_type = "Custom::Resource"
@@ -135,5 +163,15 @@ class MyMacroResource(AWSObject):
135163
}
136164

137165

166+
class MyListResource(AWSObject):
167+
resource_type = "Some::Special::Resource"
168+
169+
props = {
170+
"AZs": ([str], True),
171+
"Cidr": ([str], True),
172+
"Split": ([str], True),
173+
}
174+
175+
138176
if __name__ == "__main__":
139177
unittest.main()

troposphere/template_generator.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,21 @@
2424
from troposphere import Parameter # AWSDeclarations
2525
from troposphere import (
2626
AWSHelperFn,
27+
Cidr,
2728
Export,
29+
GetAZs,
2830
Output,
2931
Ref,
32+
Split,
3033
Tags,
3134
Template,
3235
autoscaling,
3336
cloudformation,
3437
)
3538
from troposphere.policies import CreationPolicy, UpdatePolicy
3639

40+
AWS_LIST_RETURN_FUNCTIONS = (Cidr, GetAZs, Split)
41+
3742

3843
class TemplateGenerator(Template):
3944
DEPRECATED_MODULES = ["troposphere.dynamodb2"]
@@ -221,7 +226,17 @@ def _create_instance(self, cls, args, ref=None):
221226
# a list of 1 type means we must provide a list of such objects
222227
if isinstance(args, str) or not isinstance(args, Sequence):
223228
args = [args]
224-
return [self._create_instance(cls[0], v) for v in args]
229+
230+
result = [self._create_instance(cls[0], v) for v in args]
231+
232+
# AWS functions that return lists should not be wrapped
233+
# in another list
234+
if len(result) == 1 and isinstance(
235+
result[0], AWS_LIST_RETURN_FUNCTIONS
236+
):
237+
return result[0]
238+
239+
return result
225240

226241
if isinstance(cls, Sequence) or cls not in self.inspect_members.union(
227242
self._custom_members

0 commit comments

Comments
 (0)