localstack-core 4.3.1.dev35__py3-none-any.whl → 4.3.1.dev36__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.
Files changed (23) hide show
  1. localstack/services/cloudformation/engine/entities.py +9 -4
  2. localstack/services/cloudformation/engine/v2/change_set_model.py +32 -67
  3. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +119 -487
  4. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +107 -70
  5. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +574 -0
  6. localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +6 -6
  7. localstack/services/cloudformation/v2/provider.py +39 -5
  8. localstack/services/cloudformation/v2/utils.py +5 -0
  9. localstack/services/sns/resource_providers/aws_sns_topic.py +1 -0
  10. localstack/testing/pytest/cloudformation/__init__.py +0 -0
  11. localstack/testing/pytest/cloudformation/fixtures.py +169 -0
  12. localstack/version.py +2 -2
  13. {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev36.dist-info}/METADATA +1 -1
  14. {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev36.dist-info}/RECORD +22 -18
  15. localstack_core-4.3.1.dev36.dist-info/plux.json +1 -0
  16. localstack_core-4.3.1.dev35.dist-info/plux.json +0 -1
  17. {localstack_core-4.3.1.dev35.data → localstack_core-4.3.1.dev36.data}/scripts/localstack +0 -0
  18. {localstack_core-4.3.1.dev35.data → localstack_core-4.3.1.dev36.data}/scripts/localstack-supervisor +0 -0
  19. {localstack_core-4.3.1.dev35.data → localstack_core-4.3.1.dev36.data}/scripts/localstack.bat +0 -0
  20. {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev36.dist-info}/WHEEL +0 -0
  21. {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev36.dist-info}/entry_points.txt +0 -0
  22. {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev36.dist-info}/licenses/LICENSE.txt +0 -0
  23. {localstack_core-4.3.1.dev35.dist-info → localstack_core-4.3.1.dev36.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.change_set_model_describer import (
14
- ChangeSetModelDescriber,
15
- DescribeUnit,
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(ChangeSetModelDescriber):
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.visit(self._node_template)
49
+ self.process()
50
50
  return self.resources
51
51
 
52
- def visit_node_resource(self, node_resource: NodeResource) -> DescribeUnit:
53
- resource_provider_executor = ResourceProviderExecutor(
54
- stack_name=self.stack_name, stack_id=self.stack_id
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
- # TODO: investigate effects on type changes
58
- properties_describe_unit = self.visit_node_properties(node_resource.properties)
59
- LOG.info("SRW: describe unit: %s", properties_describe_unit)
60
-
61
- action = node_resource.change_type.to_action()
62
- if action is None:
63
- raise RuntimeError(
64
- f"Action should always be present, got change type: {node_resource.change_type}"
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": "AWS::SSM::Parameter"})
130
+ resource_type = get_resource_type({"Type": resource_type})
69
131
  payload = self.create_resource_provider_payload(
70
- properties_describe_unit,
71
- action,
72
- node_resource.name,
73
- resource_type,
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(node_resource.name, {"Properties": {}})
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[node_resource.name]["Properties"].update(event.resource_model)
92
- self.resources[node_resource.name].update(extra_resource_properties)
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[node_resource.name]["LogicalResourceId"] = node_resource.name
95
- self.resources[node_resource.name]["Type"] = resource_type
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
- ) -> ResourceProviderPayload:
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
- "resourceProperties": describe_unit.after_context["Properties"],
161
- "previousResourceProperties": describe_unit.before_context["Properties"],
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": {},