localstack-core 4.3.1.dev35__py3-none-any.whl → 4.3.1.dev37__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/config.py +0 -6
- localstack/deprecations.py +14 -0
- localstack/services/cloudformation/engine/entities.py +9 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +32 -67
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +119 -487
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +107 -70
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +574 -0
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +6 -6
- localstack/services/cloudformation/v2/provider.py +39 -5
- localstack/services/cloudformation/v2/utils.py +5 -0
- localstack/services/sns/resource_providers/aws_sns_topic.py +1 -0
- localstack/testing/pytest/cloudformation/__init__.py +0 -0
- localstack/testing/pytest/cloudformation/fixtures.py +169 -0
- localstack/version.py +2 -2
- {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev37.dist-info}/METADATA +1 -1
- {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev37.dist-info}/RECORD +24 -20
- localstack_core-4.3.1.dev37.dist-info/plux.json +1 -0
- localstack_core-4.3.1.dev35.dist-info/plux.json +0 -1
- {localstack_core-4.3.1.dev35.data → localstack_core-4.3.1.dev37.data}/scripts/localstack +0 -0
- {localstack_core-4.3.1.dev35.data → localstack_core-4.3.1.dev37.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.3.1.dev35.data → localstack_core-4.3.1.dev37.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev37.dist-info}/WHEEL +0 -0
- {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev37.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev37.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev37.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,18 @@
|
|
1
1
|
import logging
|
2
2
|
import uuid
|
3
|
-
from typing import Final
|
3
|
+
from typing import Any, Final, Optional
|
4
4
|
|
5
5
|
from localstack.aws.api.cloudformation import ChangeAction
|
6
6
|
from localstack.constants import INTERNAL_AWS_SECRET_ACCESS_KEY
|
7
7
|
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
8
|
-
NodeIntrinsicFunction,
|
9
8
|
NodeResource,
|
10
9
|
NodeTemplate,
|
11
|
-
TerminalValue,
|
12
10
|
)
|
13
|
-
from localstack.services.cloudformation.engine.v2.
|
14
|
-
|
15
|
-
|
11
|
+
from localstack.services.cloudformation.engine.v2.change_set_model_preproc import (
|
12
|
+
ChangeSetModelPreproc,
|
13
|
+
PreprocEntityDelta,
|
14
|
+
PreprocProperties,
|
15
|
+
PreprocResource,
|
16
16
|
)
|
17
17
|
from localstack.services.cloudformation.resource_provider import (
|
18
18
|
Credentials,
|
@@ -26,7 +26,7 @@ from localstack.services.cloudformation.resource_provider import (
|
|
26
26
|
LOG = logging.getLogger(__name__)
|
27
27
|
|
28
28
|
|
29
|
-
class ChangeSetModelExecutor(
|
29
|
+
class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
30
30
|
account_id: Final[str]
|
31
31
|
region: Final[str]
|
32
32
|
|
@@ -46,31 +46,94 @@ class ChangeSetModelExecutor(ChangeSetModelDescriber):
|
|
46
46
|
self.resources = {}
|
47
47
|
|
48
48
|
def execute(self) -> dict:
|
49
|
-
self.
|
49
|
+
self.process()
|
50
50
|
return self.resources
|
51
51
|
|
52
|
-
def visit_node_resource(
|
53
|
-
|
54
|
-
|
52
|
+
def visit_node_resource(
|
53
|
+
self, node_resource: NodeResource
|
54
|
+
) -> PreprocEntityDelta[PreprocResource, PreprocResource]:
|
55
|
+
delta = super().visit_node_resource(node_resource=node_resource)
|
56
|
+
self._execute_on_resource_change(
|
57
|
+
name=node_resource.name, before=delta.before, after=delta.after
|
55
58
|
)
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
return delta
|
60
|
+
|
61
|
+
def _reduce_intrinsic_function_ref_value(self, preproc_value: Any) -> Any:
|
62
|
+
# TODO: this should be implemented to compute the runtime reference value for node entities.
|
63
|
+
return super()._reduce_intrinsic_function_ref_value(preproc_value=preproc_value)
|
64
|
+
|
65
|
+
def _execute_on_resource_change(
|
66
|
+
self, name: str, before: Optional[PreprocResource], after: Optional[PreprocResource]
|
67
|
+
) -> None:
|
68
|
+
# TODO: this logic is a POC and should be revised.
|
69
|
+
if before is not None and after is not None:
|
70
|
+
# Case: change on same type.
|
71
|
+
if before.resource_type == after.resource_type:
|
72
|
+
# Register a Modified if changed.
|
73
|
+
self._execute_resource_action(
|
74
|
+
action=ChangeAction.Modify,
|
75
|
+
logical_resource_id=name,
|
76
|
+
resource_type=before.resource_type,
|
77
|
+
before_properties=before.properties,
|
78
|
+
after_properties=after.properties,
|
79
|
+
)
|
80
|
+
# Case: type migration.
|
81
|
+
# TODO: Add test to assert that on type change the resources are replaced.
|
82
|
+
else:
|
83
|
+
# Register a Removed for the previous type.
|
84
|
+
self._execute_resource_action(
|
85
|
+
action=ChangeAction.Remove,
|
86
|
+
logical_resource_id=name,
|
87
|
+
resource_type=before.resource_type,
|
88
|
+
before_properties=before.properties,
|
89
|
+
after_properties=None,
|
90
|
+
)
|
91
|
+
# Register a Create for the next type.
|
92
|
+
self._execute_resource_action(
|
93
|
+
action=ChangeAction.Add,
|
94
|
+
logical_resource_id=name,
|
95
|
+
resource_type=after.resource_type,
|
96
|
+
before_properties=None,
|
97
|
+
after_properties=after.properties,
|
98
|
+
)
|
99
|
+
elif before is not None:
|
100
|
+
# Case: removal
|
101
|
+
self._execute_resource_action(
|
102
|
+
action=ChangeAction.Remove,
|
103
|
+
logical_resource_id=name,
|
104
|
+
resource_type=before.resource_type,
|
105
|
+
before_properties=before.properties,
|
106
|
+
after_properties=None,
|
107
|
+
)
|
108
|
+
elif after is not None:
|
109
|
+
# Case: addition
|
110
|
+
self._execute_resource_action(
|
111
|
+
action=ChangeAction.Add,
|
112
|
+
logical_resource_id=name,
|
113
|
+
resource_type=after.resource_type,
|
114
|
+
before_properties=None,
|
115
|
+
after_properties=after.properties,
|
65
116
|
)
|
66
117
|
|
118
|
+
def _execute_resource_action(
|
119
|
+
self,
|
120
|
+
action: ChangeAction,
|
121
|
+
logical_resource_id: str,
|
122
|
+
resource_type: str,
|
123
|
+
before_properties: Optional[PreprocProperties],
|
124
|
+
after_properties: Optional[PreprocProperties],
|
125
|
+
) -> None:
|
126
|
+
resource_provider_executor = ResourceProviderExecutor(
|
127
|
+
stack_name=self.stack_name, stack_id=self.stack_id
|
128
|
+
)
|
67
129
|
# TODO
|
68
|
-
resource_type = get_resource_type({"Type":
|
130
|
+
resource_type = get_resource_type({"Type": resource_type})
|
69
131
|
payload = self.create_resource_provider_payload(
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
132
|
+
action=action,
|
133
|
+
logical_resource_id=logical_resource_id,
|
134
|
+
resource_type=resource_type,
|
135
|
+
before_properties=before_properties,
|
136
|
+
after_properties=after_properties,
|
74
137
|
)
|
75
138
|
resource_provider = resource_provider_executor.try_load_resource_provider(resource_type)
|
76
139
|
|
@@ -83,68 +146,41 @@ class ChangeSetModelExecutor(ChangeSetModelDescriber):
|
|
83
146
|
else:
|
84
147
|
event = ProgressEvent(OperationStatus.SUCCESS, resource_model={})
|
85
148
|
|
86
|
-
self.resources.setdefault(
|
149
|
+
self.resources.setdefault(logical_resource_id, {"Properties": {}})
|
87
150
|
match event.status:
|
88
151
|
case OperationStatus.SUCCESS:
|
89
152
|
# merge the resources state with the external state
|
90
153
|
# TODO: this is likely a duplicate of updating from extra_resource_properties
|
91
|
-
self.resources[
|
92
|
-
self.resources[
|
154
|
+
self.resources[logical_resource_id]["Properties"].update(event.resource_model)
|
155
|
+
self.resources[logical_resource_id].update(extra_resource_properties)
|
93
156
|
# XXX for legacy delete_stack compatibility
|
94
|
-
self.resources[
|
95
|
-
self.resources[
|
157
|
+
self.resources[logical_resource_id]["LogicalResourceId"] = logical_resource_id
|
158
|
+
self.resources[logical_resource_id]["Type"] = resource_type
|
96
159
|
case any:
|
97
160
|
raise NotImplementedError(f"Event status '{any}' not handled")
|
98
161
|
|
99
|
-
return DescribeUnit(before_context=None, after_context={})
|
100
|
-
|
101
|
-
def visit_node_intrinsic_function_fn_get_att(
|
102
|
-
self, node_intrinsic_function: NodeIntrinsicFunction
|
103
|
-
) -> DescribeUnit:
|
104
|
-
arguments_unit = self.visit(node_intrinsic_function.arguments)
|
105
|
-
before_arguments_list = arguments_unit.before_context
|
106
|
-
after_arguments_list = arguments_unit.after_context
|
107
|
-
if before_arguments_list:
|
108
|
-
logical_name_of_resource = before_arguments_list[0]
|
109
|
-
attribute_name = before_arguments_list[1]
|
110
|
-
before_node_resource = self._get_node_resource_for(
|
111
|
-
resource_name=logical_name_of_resource, node_template=self._node_template
|
112
|
-
)
|
113
|
-
node_property: TerminalValue = self._get_node_property_for(
|
114
|
-
property_name=attribute_name, node_resource=before_node_resource
|
115
|
-
)
|
116
|
-
before_context = self.visit(node_property.value).before_context
|
117
|
-
else:
|
118
|
-
before_context = None
|
119
|
-
|
120
|
-
if after_arguments_list:
|
121
|
-
logical_name_of_resource = after_arguments_list[0]
|
122
|
-
attribute_name = after_arguments_list[1]
|
123
|
-
after_node_resource = self._get_node_resource_for(
|
124
|
-
resource_name=logical_name_of_resource, node_template=self._node_template
|
125
|
-
)
|
126
|
-
node_property: TerminalValue = self._get_node_property_for(
|
127
|
-
property_name=attribute_name, node_resource=after_node_resource
|
128
|
-
)
|
129
|
-
after_context = self.visit(node_property.value).after_context
|
130
|
-
else:
|
131
|
-
after_context = None
|
132
|
-
|
133
|
-
return DescribeUnit(before_context=before_context, after_context=after_context)
|
134
|
-
|
135
162
|
def create_resource_provider_payload(
|
136
163
|
self,
|
137
|
-
describe_unit: DescribeUnit,
|
138
164
|
action: ChangeAction,
|
139
165
|
logical_resource_id: str,
|
140
166
|
resource_type: str,
|
141
|
-
|
167
|
+
before_properties: Optional[PreprocProperties],
|
168
|
+
after_properties: Optional[PreprocProperties],
|
169
|
+
) -> Optional[ResourceProviderPayload]:
|
142
170
|
# FIXME: use proper credentials
|
143
171
|
creds: Credentials = {
|
144
172
|
"accessKeyId": self.account_id,
|
145
173
|
"secretAccessKey": INTERNAL_AWS_SECRET_ACCESS_KEY,
|
146
174
|
"sessionToken": "",
|
147
175
|
}
|
176
|
+
before_properties_value = before_properties.properties if before_properties else None
|
177
|
+
if action == ChangeAction.Remove:
|
178
|
+
resource_properties = before_properties_value
|
179
|
+
previous_resource_properties = None
|
180
|
+
else:
|
181
|
+
after_properties_value = after_properties.properties if after_properties else None
|
182
|
+
resource_properties = after_properties_value
|
183
|
+
previous_resource_properties = before_properties_value
|
148
184
|
resource_provider_payload: ResourceProviderPayload = {
|
149
185
|
"awsAccountId": self.account_id,
|
150
186
|
"callbackContext": {},
|
@@ -157,8 +193,9 @@ class ChangeSetModelExecutor(ChangeSetModelDescriber):
|
|
157
193
|
"action": str(action),
|
158
194
|
"requestData": {
|
159
195
|
"logicalResourceId": logical_resource_id,
|
160
|
-
|
161
|
-
"
|
196
|
+
# TODO: assign before and previous according on the action type.
|
197
|
+
"resourceProperties": resource_properties,
|
198
|
+
"previousResourceProperties": previous_resource_properties,
|
162
199
|
"callerCredentials": creds,
|
163
200
|
"providerCredentials": creds,
|
164
201
|
"systemTags": {},
|