localstack-core 4.4.1.dev59__py3-none-any.whl → 4.4.1.dev65__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack/aws/api/ec2/__init__.py +70 -2
- localstack/aws/api/s3/__init__.py +2 -0
- localstack/services/cloudformation/engine/v2/change_set_model.py +107 -146
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +41 -22
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +51 -23
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +244 -123
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +16 -1
- localstack/services/s3/provider.py +3 -2
- localstack/services/sns/provider.py +27 -9
- localstack/testing/pytest/cloudformation/fixtures.py +13 -1
- localstack/version.py +2 -2
- {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/METADATA +3 -3
- {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/RECORD +21 -21
- localstack_core-4.4.1.dev65.dist-info/plux.json +1 -0
- localstack_core-4.4.1.dev59.dist-info/plux.json +0 -1
- {localstack_core-4.4.1.dev59.data → localstack_core-4.4.1.dev65.data}/scripts/localstack +0 -0
- {localstack_core-4.4.1.dev59.data → localstack_core-4.4.1.dev65.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.4.1.dev59.data → localstack_core-4.4.1.dev65.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/WHEEL +0 -0
- {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/top_level.txt +0 -0
@@ -6,8 +6,11 @@ from typing import Final, Optional
|
|
6
6
|
import localstack.aws.api.cloudformation as cfn_api
|
7
7
|
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
8
8
|
NodeIntrinsicFunction,
|
9
|
+
NodeProperty,
|
9
10
|
NodeResource,
|
11
|
+
Nothing,
|
10
12
|
PropertiesKey,
|
13
|
+
is_nothing,
|
11
14
|
)
|
12
15
|
from localstack.services.cloudformation.engine.v2.change_set_model_preproc import (
|
13
16
|
ChangeSetModelPreproc,
|
@@ -45,26 +48,36 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
|
|
45
48
|
# artificially limit the precision of our output to match AWS's?
|
46
49
|
|
47
50
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if
|
53
|
-
|
54
|
-
|
51
|
+
before_argument: Optional[list[str]] = arguments_delta.before
|
52
|
+
if isinstance(before_argument, str):
|
53
|
+
before_argument = before_argument.split(".")
|
54
|
+
after_argument: Optional[list[str]] = arguments_delta.after
|
55
|
+
if isinstance(after_argument, str):
|
56
|
+
after_argument = after_argument.split(".")
|
57
|
+
|
58
|
+
before = Nothing
|
59
|
+
if not is_nothing(before_argument):
|
60
|
+
before_logical_name_of_resource = before_argument[0]
|
61
|
+
before_attribute_name = before_argument[1]
|
55
62
|
before_node_resource = self._get_node_resource_for(
|
56
63
|
resource_name=before_logical_name_of_resource, node_template=self._node_template
|
57
64
|
)
|
58
|
-
before_node_property = self._get_node_property_for(
|
65
|
+
before_node_property: Optional[NodeProperty] = self._get_node_property_for(
|
59
66
|
property_name=before_attribute_name, node_resource=before_node_resource
|
60
67
|
)
|
61
|
-
|
62
|
-
|
68
|
+
if before_node_property is not None:
|
69
|
+
before_property_delta = self.visit(before_node_property)
|
70
|
+
before = before_property_delta.before
|
71
|
+
else:
|
72
|
+
before = self._before_deployed_property_value_of(
|
73
|
+
resource_logical_id=before_logical_name_of_resource,
|
74
|
+
property_name=before_attribute_name,
|
75
|
+
)
|
63
76
|
|
64
|
-
after =
|
65
|
-
if
|
66
|
-
after_logical_name_of_resource =
|
67
|
-
after_attribute_name =
|
77
|
+
after = Nothing
|
78
|
+
if not is_nothing(after_argument):
|
79
|
+
after_logical_name_of_resource = after_argument[0]
|
80
|
+
after_attribute_name = after_argument[1]
|
68
81
|
after_node_resource = self._get_node_resource_for(
|
69
82
|
resource_name=after_logical_name_of_resource, node_template=self._node_template
|
70
83
|
)
|
@@ -74,12 +87,18 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
|
|
74
87
|
)
|
75
88
|
if after_node_property is not None:
|
76
89
|
after_property_delta = self.visit(after_node_property)
|
90
|
+
if after_property_delta.before == after_property_delta.after:
|
91
|
+
after = after_property_delta.after
|
92
|
+
else:
|
93
|
+
after = CHANGESET_KNOWN_AFTER_APPLY
|
77
94
|
else:
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
95
|
+
try:
|
96
|
+
after = self._after_deployed_property_value_of(
|
97
|
+
resource_logical_id=after_logical_name_of_resource,
|
98
|
+
property_name=after_attribute_name,
|
99
|
+
)
|
100
|
+
except RuntimeError:
|
101
|
+
after = CHANGESET_KNOWN_AFTER_APPLY
|
83
102
|
|
84
103
|
return PreprocEntityDelta(before=before, after=after)
|
85
104
|
|
@@ -137,7 +156,7 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
|
|
137
156
|
if before == after:
|
138
157
|
# unchanged: nothing to do.
|
139
158
|
return
|
140
|
-
if
|
159
|
+
if not is_nothing(before) and not is_nothing(after):
|
141
160
|
# Case: change on same type.
|
142
161
|
if before.resource_type == after.resource_type:
|
143
162
|
# Register a Modified if changed.
|
@@ -167,7 +186,7 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
|
|
167
186
|
before_properties=None,
|
168
187
|
after_properties=after.properties,
|
169
188
|
)
|
170
|
-
elif
|
189
|
+
elif not is_nothing(before):
|
171
190
|
# Case: removal
|
172
191
|
self._register_resource_change(
|
173
192
|
logical_id=name,
|
@@ -176,7 +195,7 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
|
|
176
195
|
before_properties=before.properties,
|
177
196
|
after_properties=None,
|
178
197
|
)
|
179
|
-
elif
|
198
|
+
elif not is_nothing(after):
|
180
199
|
# Case: addition
|
181
200
|
self._register_resource_change(
|
182
201
|
logical_id=name,
|
@@ -11,6 +11,7 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
11
11
|
NodeOutput,
|
12
12
|
NodeParameter,
|
13
13
|
NodeResource,
|
14
|
+
is_nothing,
|
14
15
|
)
|
15
16
|
from localstack.services.cloudformation.engine.v2.change_set_model_preproc import (
|
16
17
|
ChangeSetModelPreproc,
|
@@ -103,42 +104,46 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
103
104
|
`after` delta with the physical resource ID, if side effects resulted in an update.
|
104
105
|
"""
|
105
106
|
delta = super().visit_node_resource(node_resource=node_resource)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
107
|
+
before = delta.before
|
108
|
+
after = delta.after
|
109
|
+
|
110
|
+
if before != after:
|
111
|
+
# There are changes for this resource.
|
112
|
+
self._execute_resource_change(name=node_resource.name, before=before, after=after)
|
113
|
+
else:
|
114
|
+
# There are no updates for this resource; iff the resource was previously
|
115
|
+
# deployed, then the resolved details are copied in the current state for
|
116
|
+
# references or other downstream operations.
|
117
|
+
if not is_nothing(before):
|
118
|
+
before_logical_id = delta.before.logical_id
|
119
|
+
before_resource = self._before_resolved_resources.get(before_logical_id, dict())
|
120
|
+
self.resources[before_logical_id] = before_resource
|
121
|
+
|
122
|
+
# Update the latest version of this resource for downstream references.
|
123
|
+
if not is_nothing(after):
|
124
|
+
after_logical_id = after.logical_id
|
125
|
+
after_physical_id: str = self._after_resource_physical_id(
|
113
126
|
resource_logical_id=after_logical_id
|
114
127
|
)
|
115
|
-
|
116
|
-
raise RuntimeError(
|
117
|
-
f"No PhysicalResourceId was found for resource '{after_physical_id}' post-update."
|
118
|
-
)
|
119
|
-
after_resource.physical_resource_id = after_physical_id
|
128
|
+
after.physical_resource_id = after_physical_id
|
120
129
|
return delta
|
121
130
|
|
122
131
|
def visit_node_output(
|
123
132
|
self, node_output: NodeOutput
|
124
133
|
) -> PreprocEntityDelta[PreprocOutput, PreprocOutput]:
|
125
134
|
delta = super().visit_node_output(node_output=node_output)
|
126
|
-
|
127
|
-
|
128
|
-
# TODO: are there other situations?
|
135
|
+
after = delta.after
|
136
|
+
if is_nothing(after) or (isinstance(after, PreprocOutput) and after.condition is False):
|
129
137
|
return delta
|
130
|
-
|
131
138
|
self.outputs[delta.after.name] = delta.after.value
|
132
139
|
return delta
|
133
140
|
|
134
|
-
def
|
141
|
+
def _execute_resource_change(
|
135
142
|
self, name: str, before: Optional[PreprocResource], after: Optional[PreprocResource]
|
136
143
|
) -> None:
|
137
|
-
|
138
|
-
# unchanged: nothing to do.
|
139
|
-
return
|
144
|
+
# Changes are to be made about this resource.
|
140
145
|
# TODO: this logic is a POC and should be revised.
|
141
|
-
if
|
146
|
+
if not is_nothing(before) and not is_nothing(after):
|
142
147
|
# Case: change on same type.
|
143
148
|
if before.resource_type == after.resource_type:
|
144
149
|
# Register a Modified if changed.
|
@@ -173,7 +178,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
173
178
|
before_properties=None,
|
174
179
|
after_properties=after.properties,
|
175
180
|
)
|
176
|
-
elif
|
181
|
+
elif not is_nothing(before):
|
177
182
|
# Case: removal
|
178
183
|
# XXX hacky, stick the previous resources' properties into the payload
|
179
184
|
# XXX hacky, stick the previous resources' properties into the payload
|
@@ -186,7 +191,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
186
191
|
before_properties=before_properties,
|
187
192
|
after_properties=None,
|
188
193
|
)
|
189
|
-
elif
|
194
|
+
elif not is_nothing(after):
|
190
195
|
# Case: addition
|
191
196
|
self._execute_resource_action(
|
192
197
|
action=ChangeAction.Add,
|
@@ -257,11 +262,34 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
257
262
|
case OperationStatus.SUCCESS:
|
258
263
|
# merge the resources state with the external state
|
259
264
|
# TODO: this is likely a duplicate of updating from extra_resource_properties
|
265
|
+
|
266
|
+
# TODO: add typing
|
267
|
+
# TODO: avoid the use of string literals for sampling from the object, use typed classes instead
|
268
|
+
# TODO: avoid sampling from resources and use tmp var reference
|
269
|
+
# TODO: add utils functions to abstract this logic away (resource.update(..))
|
270
|
+
# TODO: avoid the use of setdefault (debuggability/readability)
|
271
|
+
# TODO: review the use of merge
|
272
|
+
|
260
273
|
self.resources[logical_resource_id]["Properties"].update(event.resource_model)
|
261
274
|
self.resources[logical_resource_id].update(extra_resource_properties)
|
262
275
|
# XXX for legacy delete_stack compatibility
|
263
276
|
self.resources[logical_resource_id]["LogicalResourceId"] = logical_resource_id
|
264
277
|
self.resources[logical_resource_id]["Type"] = resource_type
|
278
|
+
|
279
|
+
# TODO: review why the physical id is returned as None during updates
|
280
|
+
# TODO: abstract this in member function of resource classes instead
|
281
|
+
physical_resource_id = None
|
282
|
+
try:
|
283
|
+
physical_resource_id = self._after_resource_physical_id(logical_resource_id)
|
284
|
+
except RuntimeError:
|
285
|
+
# The physical id is missing or is set to None, which is invalid.
|
286
|
+
pass
|
287
|
+
if physical_resource_id is None:
|
288
|
+
# The physical resource id is None after an update that didn't rewrite the resource, the previous
|
289
|
+
# resource id is therefore the current physical id of this resource.
|
290
|
+
physical_resource_id = self._before_resource_physical_id(logical_resource_id)
|
291
|
+
self.resources[logical_resource_id]["PhysicalResourceId"] = physical_resource_id
|
292
|
+
|
265
293
|
case OperationStatus.FAILED:
|
266
294
|
reason = event.message
|
267
295
|
LOG.warning(
|