Skip to content

Commit 337a740

Browse files
committed
Merge branch 'morosi-feat' into 'master'
Add update_if_exist option to add_output/parameter See merge request it/e3-aws!91
2 parents d2c8f2d + bdff657 commit 337a740

File tree

2 files changed

+73
-8
lines changed

2 files changed

+73
-8
lines changed

src/e3/aws/troposphere/__init__.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ def resources(self, stack: Stack) -> list[AWSObject | Construct]:
118118
# Add the parameter during template creation even if the S3 key may not
119119
# be known yet. This is useful if creating a stack from code, so that
120120
# the exported template contains the parameter
121-
stack.add_parameter(self.s3_key_parameter)
121+
stack.add_parameter(
122+
self.s3_key_parameter,
123+
update_if_exist=True,
124+
)
122125
# Adding the Output can be useful for Lambda function with versioning.
123126
# The exported value can be retrieved by the lambda without the need to update
124127
# the lambda version.
@@ -128,7 +131,8 @@ def resources(self, stack: Stack) -> list[AWSObject | Construct]:
128131
Description=f"S3 URI for the Asset {self.name}",
129132
Export=Export(name=self.s3_uri_output_name),
130133
Value=f"s3://{stack.s3_bucket}/{stack.s3_assets_key}{self.s3_key}",
131-
)
134+
),
135+
update_if_exist=True,
132136
)
133137
return []
134138

@@ -276,7 +280,7 @@ def add(self, element: AWSObject | Construct | Stack) -> Stack:
276280
# Special case to keep track of Assets and generate parameters
277281
# for the S3 keys
278282
if isinstance(construct, Asset):
279-
self.add_parameter(construct.s3_key_parameter)
283+
self.add_parameter(construct.s3_key_parameter, update_if_exist=True)
280284
self.assets[construct.name] = construct
281285

282286
constructs_to_objects.extend(construct.resources(stack=self))
@@ -295,26 +299,41 @@ def extend(self, elements: Iterable[AWSObject | Construct | Stack]) -> Stack:
295299

296300
return self
297301

298-
def add_parameter(self, parameter: Parameter | list[Parameter]) -> None:
302+
def add_parameter(
303+
self, parameter: Parameter | list[Parameter], update_if_exist: bool = False
304+
) -> None:
299305
"""Add parameters to stack template.
300306
301307
:param parameter: parameter to add to the template
308+
:param update_if_exist: update the parameter if already exists, avoiding
309+
the duplicate key exception
302310
"""
303311
if not isinstance(parameter, list):
304312
parameter = [parameter]
305313

306314
for param in parameter:
307-
if param.title in self.template.parameters:
315+
if update_if_exist and param.title in self.template.parameters:
308316
self.template.parameters[param.title] = param
309317
else:
310318
self.template.add_parameter(param)
311319

312-
def add_output(self, output: Output | list[Output]) -> None:
320+
def add_output(
321+
self, output: Output | list[Output], update_if_exist: bool = False
322+
) -> None:
313323
"""Add outputs to stack template.
314324
315325
:param output: output to add to the template
326+
:param update_if_exist: update the output if already exists, avoiding
327+
the duplicate key exception
316328
"""
317-
self.template.add_output(output)
329+
if not isinstance(output, list):
330+
output = [output]
331+
332+
for out in output:
333+
if update_if_exist and out.title in self.template.outputs:
334+
self.template.outputs[out.title] = out
335+
else:
336+
self.template.add_output(out)
318337

319338
def add_condition(self, condition_name: str, condition: ConditionFunction) -> None:
320339
"""Add condition to stack template.

tests/tests_e3_aws/troposphere/stack/stack_test.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import json
44
from pathlib import Path
5-
5+
import pytest
66
from troposphere import Parameter, Output, Export
77

88
from e3.aws.troposphere.s3.bucket import Bucket
@@ -59,6 +59,28 @@ def test_add_parameters() -> None:
5959
assert stack.export()["Parameters"] == expected_template
6060

6161

62+
def test_update_parameter() -> None:
63+
"""Test updating an already existing parameter."""
64+
p = Parameter("Parameter", Description="My parameter", Type="String")
65+
stack = Stack("test-stack", "this is a test stack")
66+
stack.add_parameter(p)
67+
68+
# This one should fail because of the duplicate key
69+
with pytest.raises(match='duplicate key "Parameter" detected'):
70+
stack.add_parameter(p)
71+
72+
# This one should update the parameter
73+
p.Description = "Updated parameter"
74+
stack.add_parameter(p, update_if_exist=True)
75+
76+
assert stack.export()["Parameters"] == {
77+
"Parameter": {
78+
"Description": "Updated parameter",
79+
"Type": "String",
80+
},
81+
}
82+
83+
6284
def test_add_outputs() -> None:
6385
"""Test adding outputs to a stack."""
6486
stack = Stack("test-stack", "this is a test stack")
@@ -85,6 +107,30 @@ def test_add_outputs() -> None:
85107
assert stack.export()["Resources"] == expected_template
86108

87109

110+
def test_update_output() -> None:
111+
"""Test updating an already existing output."""
112+
o = Output("Output", Description="My output", Value=Export(name="Output"))
113+
stack = Stack("test-stack", "this is a test stack")
114+
stack.add_output(o)
115+
116+
# This one should fail because of the duplicate key
117+
with pytest.raises(match='duplicate key "Output" detected'):
118+
stack.add_output(o)
119+
120+
# This one should update the output
121+
o.Description = "Updated output"
122+
stack.add_output(o, update_if_exist=True)
123+
124+
assert stack.export()["Outputs"] == {
125+
"Output": {
126+
"Description": "Updated output",
127+
"Value": {
128+
"Name": "Output",
129+
},
130+
},
131+
}
132+
133+
88134
def test_extend() -> None:
89135
"""Test adding multiple construct and retrieving an AWSObject from a stack."""
90136
stack = Stack("test-stack", "this is a test stack")

0 commit comments

Comments
 (0)