Skip to content

Commit 2847084

Browse files
authored
Merge pull request #279 from DontShaveTheYak/develop
Release v0.7.0
2 parents 0c2c827 + 9840d86 commit 2847084

File tree

3 files changed

+87
-24
lines changed

3 files changed

+87
-24
lines changed

src/cf2tf/conversion/expressions.py

+62-10
Original file line numberDiff line numberDiff line change
@@ -359,19 +359,68 @@ def get_att(template: "TemplateConverter", values: Any):
359359
values (Any): The values passed to the function.
360360
361361
Raises:
362-
TypeError: If values is not a list.
363-
ValueError: If length of values is not 3.
364-
TypeError: If the logicalNameOfResource and attributeName are not str.
365-
KeyError: If the logicalNameOfResource is not found in the template.
362+
TypeError: If values is not a String.
366363
367364
Returns:
368-
str: Terraform equivalent expression.
365+
LiteralType: Terraform equivalent expression.
369366
"""
370367

371-
if not isinstance(values, List):
372-
raise TypeError(
373-
f"Fn::GetAtt - The values must be a List, not {type(values).__name__}."
368+
if isinstance(values, (str, StringType)):
369+
return _get_att_string(template, values)
370+
371+
if isinstance(values, list):
372+
return _get_att_list(template, values)
373+
374+
raise TypeError(
375+
f"Fn::GetAtt - The value must be a String or List, not {type(values).__name__}."
376+
)
377+
378+
379+
def _get_att_string(template: "TemplateConverter", values: Any):
380+
"""Converts AWS GetAtt intrinsic function to it's Terraform equivalent.
381+
382+
Args:
383+
template (Configuration): The template being tested.
384+
values (Any): The values passed to the function.
385+
386+
Raises:
387+
ValueError: If the value doesn't contain a resource id and an attribute.
388+
389+
Returns:
390+
LiteralType: Terraform equivalent expression.
391+
"""
392+
393+
if "." not in values:
394+
raise ValueError(
395+
"Fn::GetAtt - The value must contain a resource id and an attribute."
374396
)
397+
398+
parts = values.split(".")
399+
400+
resouce_id = parts[0]
401+
402+
attributes = ".".join(parts[1:])
403+
404+
result = _get_att_list(template, [resouce_id, attributes])
405+
406+
return result
407+
408+
409+
def _get_att_list(template: "TemplateConverter", values: Any):
410+
"""Converts AWS GetAtt intrinsic function to it's Terraform equivalent.
411+
412+
Args:
413+
template (Configuration): The template being tested.
414+
values (Any): The values passed to the function.
415+
416+
Raises:
417+
ValueError: If the values length is not 2.
418+
TypeError: If the values are not strings.
419+
420+
Returns:
421+
LiteralType: Terraform equivalent expression.
422+
"""
423+
375424
if not len(values) == 2:
376425
raise ValueError(
377426
(
@@ -712,7 +761,7 @@ def replace_var(m):
712761

713762
attributes = ".".join(parts[1:])
714763

715-
result = get_att(template, [resouce_id, attributes])
764+
result = _get_att_list(template, [resouce_id, attributes])
716765
else:
717766
result = ref(template, var)
718767

@@ -775,7 +824,7 @@ def replace_var(m) -> str:
775824

776825
attributes = ".".join(parts[1:])
777826

778-
result = get_att(template, [resouce_id, attributes])
827+
result = _get_att_list(template, [resouce_id, attributes])
779828
else:
780829
result = ref(template, var)
781830

@@ -982,6 +1031,7 @@ def wrap_in_curlys(input: str):
9821031
**ALLOWED_NESTED_CONDITIONS,
9831032
"Fn::Join": join,
9841033
"Fn::Select": select,
1034+
"Fn::Sub": sub,
9851035
},
9861036
"Fn::If": {
9871037
"Fn::Base64": base64,
@@ -993,6 +1043,7 @@ def wrap_in_curlys(input: str):
9931043
"Fn::Select": select,
9941044
"Fn::Sub": sub,
9951045
"Ref": ref,
1046+
"Fn::Split": split,
9961047
},
9971048
"Fn::Not": ALLOWED_NESTED_CONDITIONS,
9981049
"Fn::Or": ALLOWED_NESTED_CONDITIONS,
@@ -1001,6 +1052,7 @@ def wrap_in_curlys(input: str):
10011052
"Fn::Cidr": {
10021053
"Fn::Select": select,
10031054
"Ref": ref,
1055+
"Fn::GetAtt": get_att,
10041056
},
10051057
"Fn::FindInMap": {
10061058
"Fn::FindInMap": find_in_map,

src/cf2tf/convert.py

+9-11
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,7 @@ def resolve_values( # noqa: max-complexity=13
173173
# This takes care of keys that not intrinsic functions,
174174
# except for the condition func
175175
if "Fn::" not in key and key != "Condition":
176-
data[key] = self.resolve_values(
177-
value,
178-
allowed_func,
179-
)
176+
data[key] = self.resolve_values(value, allowed_func, prev_func)
180177
continue
181178

182179
# Takes care of the tricky 'Condition' key
@@ -194,23 +191,24 @@ def resolve_values( # noqa: max-complexity=13
194191
return functions.condition(self, value)
195192

196193
# Normal key like in an IAM role
197-
data[key] = self.resolve_values(
198-
value,
199-
allowed_func,
200-
)
194+
data[key] = self.resolve_values(value, allowed_func, prev_func)
201195
continue
202196

203197
if key not in allowed_func:
204198
raise ValueError(f"{key} not allowed to be nested in {prev_func}.")
205199

200+
prev_func = key
201+
206202
value = self.resolve_values(
207-
value, functions.ALLOWED_FUNCTIONS[key], key
203+
value, functions.ALLOWED_FUNCTIONS[key], prev_func
208204
)
209205

210206
try:
211207
return allowed_func[key](self, value)
212-
except Exception:
213-
return CommentType(f"Unable to resolve {key} with value: {value}")
208+
except Exception as e:
209+
return CommentType(
210+
f"Unable to resolve {key} with value: {value} because {e}"
211+
)
214212

215213
return MapType(data)
216214
elif isinstance(data, list):

tests/test_conversion/test_expressions.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,20 @@ def test_find_in_map(fake_tc: TemplateConverter):
298298

299299

300300
def test_get_att(fake_tc: TemplateConverter):
301-
# Test that it will only take a list
301+
302302
with pytest.raises(TypeError) as type_error:
303303
expressions.get_att(fake_tc, {})
304304

305-
assert "Fn::GetAtt - The values must be a List, not dict." in str(type_error)
305+
assert "Fn::GetAtt - The value must be a String or List, not dict." in str(
306+
type_error
307+
)
308+
# Test that string must be in the correct format
309+
with pytest.raises(ValueError) as value_error:
310+
expressions.get_att(fake_tc, "foo")
311+
312+
assert "Fn::GetAtt - The value must contain a resource id and an attribute." in str(
313+
value_error
314+
)
306315

307316
# Test that list size must be two
308317
with pytest.raises(ValueError) as value_error:
@@ -343,6 +352,10 @@ def test_get_att(fake_tc: TemplateConverter):
343352

344353
assert result == expected_result
345354

355+
result = expressions.get_att(fake_tc, f"{resource_id}.{test_attr}")
356+
357+
assert result == expected_result
358+
346359

347360
def test_get_att_nested(fake_tc: TemplateConverter):
348361
"""Test that nested cloudformation attributes work."""
@@ -394,7 +407,7 @@ def test_get_att_nested(fake_tc: TemplateConverter):
394407

395408
expected_result = f"aws_cloudformation_stack.{resource_id}.{test_attr.lower()}"
396409

397-
result = expressions.get_att(fake_tc, [resource_id, test_attr])
410+
result = expressions.get_att(fake_tc, f"{resource_id}.{test_attr}")
398411

399412
assert result == expected_result
400413

0 commit comments

Comments
 (0)