localstack-core 4.3.1.dev5__py3-none-any.whl → 4.3.1.dev27__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/services/cloudformation/engine/entities.py +18 -1
- localstack/services/cloudformation/engine/template_deployer.py +0 -9
- localstack/services/cloudformation/engine/v2/change_set_model.py +281 -36
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +187 -70
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +170 -0
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +21 -0
- localstack/services/cloudformation/v2/provider.py +72 -6
- localstack/services/ec2/patches.py +31 -3
- localstack/services/kms/models.py +1 -1
- localstack/services/lambda_/event_source_mapping/pollers/dynamodb_poller.py +2 -0
- localstack/services/lambda_/event_source_mapping/pollers/kinesis_poller.py +2 -0
- localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +4 -2
- localstack/services/lambda_/invocation/assignment.py +4 -2
- localstack/services/lambda_/invocation/execution_environment.py +16 -4
- localstack/services/lambda_/invocation/logs.py +28 -4
- localstack/services/lambda_/provider.py +18 -3
- localstack/services/lambda_/runtimes.py +15 -2
- localstack/services/s3/presigned_url.py +15 -11
- localstack/services/secretsmanager/provider.py +13 -4
- localstack/services/sqs/models.py +22 -3
- localstack/services/sqs/utils.py +16 -7
- localstack/services/ssm/resource_providers/aws_ssm_parameter.py +1 -5
- localstack/services/stepfunctions/asl/utils/json_path.py +9 -0
- localstack/testing/snapshots/transformer_utility.py +13 -0
- localstack/utils/aws/client_types.py +8 -0
- localstack/utils/docker_utils.py +2 -2
- localstack/version.py +2 -2
- {localstack_core-4.3.1.dev5.dist-info → localstack_core-4.3.1.dev27.dist-info}/METADATA +3 -3
- {localstack_core-4.3.1.dev5.dist-info → localstack_core-4.3.1.dev27.dist-info}/RECORD +37 -36
- localstack_core-4.3.1.dev27.dist-info/plux.json +1 -0
- localstack_core-4.3.1.dev5.dist-info/plux.json +0 -1
- {localstack_core-4.3.1.dev5.data → localstack_core-4.3.1.dev27.data}/scripts/localstack +0 -0
- {localstack_core-4.3.1.dev5.data → localstack_core-4.3.1.dev27.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.3.1.dev5.data → localstack_core-4.3.1.dev27.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.3.1.dev5.dist-info → localstack_core-4.3.1.dev27.dist-info}/WHEEL +0 -0
- {localstack_core-4.3.1.dev5.dist-info → localstack_core-4.3.1.dev27.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.3.1.dev5.dist-info → localstack_core-4.3.1.dev27.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.3.1.dev5.dist-info → localstack_core-4.3.1.dev27.dist-info}/top_level.txt +0 -0
@@ -7,11 +7,16 @@ import localstack.aws.api.cloudformation as cfn_api
|
|
7
7
|
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
8
8
|
ChangeSetEntity,
|
9
9
|
ChangeType,
|
10
|
+
ConditionKey,
|
11
|
+
ExportKey,
|
10
12
|
NodeArray,
|
11
13
|
NodeCondition,
|
12
14
|
NodeDivergence,
|
13
15
|
NodeIntrinsicFunction,
|
16
|
+
NodeMapping,
|
14
17
|
NodeObject,
|
18
|
+
NodeOutput,
|
19
|
+
NodeOutputs,
|
15
20
|
NodeParameter,
|
16
21
|
NodeProperties,
|
17
22
|
NodeProperty,
|
@@ -25,6 +30,7 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
25
30
|
TerminalValueModified,
|
26
31
|
TerminalValueRemoved,
|
27
32
|
TerminalValueUnchanged,
|
33
|
+
ValueKey,
|
28
34
|
)
|
29
35
|
from localstack.services.cloudformation.engine.v2.change_set_model_visitor import (
|
30
36
|
ChangeSetModelVisitor,
|
@@ -46,14 +52,20 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
46
52
|
_node_template: Final[NodeTemplate]
|
47
53
|
_changes: Final[cfn_api.Changes]
|
48
54
|
_describe_unit_cache: dict[Scope, DescribeUnit]
|
55
|
+
_include_property_values: Final[cfn_api.IncludePropertyValues | None]
|
49
56
|
|
50
|
-
def __init__(
|
57
|
+
def __init__(
|
58
|
+
self,
|
59
|
+
node_template: NodeTemplate,
|
60
|
+
include_property_values: cfn_api.IncludePropertyValues | None = None,
|
61
|
+
):
|
51
62
|
self._node_template = node_template
|
52
63
|
self._changes = list()
|
53
64
|
self._describe_unit_cache = dict()
|
54
|
-
self.
|
65
|
+
self._include_property_values = include_property_values
|
55
66
|
|
56
67
|
def get_changes(self) -> cfn_api.Changes:
|
68
|
+
self.visit(self._node_template)
|
57
69
|
return self._changes
|
58
70
|
|
59
71
|
@staticmethod
|
@@ -74,6 +86,15 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
74
86
|
# TODO
|
75
87
|
raise RuntimeError()
|
76
88
|
|
89
|
+
def _get_node_mapping(self, map_name: str) -> NodeMapping:
|
90
|
+
mappings: list[NodeMapping] = self._node_template.mappings.mappings
|
91
|
+
# TODO: another scenarios suggesting property lookups might be preferable.
|
92
|
+
for mapping in mappings:
|
93
|
+
if mapping.name == map_name:
|
94
|
+
return mapping
|
95
|
+
# TODO
|
96
|
+
raise RuntimeError()
|
97
|
+
|
77
98
|
def _get_node_parameter_if_exists(self, parameter_name: str) -> Optional[NodeParameter]:
|
78
99
|
parameters: list[NodeParameter] = self._node_template.parameters.parameters
|
79
100
|
# TODO: another scenarios suggesting property lookups might be preferable.
|
@@ -102,12 +123,23 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
102
123
|
return parameter_unit
|
103
124
|
|
104
125
|
# TODO: check for KNOWN AFTER APPLY values for logical ids coming from intrinsic functions as arguments.
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
126
|
+
node_resource = self._get_node_resource_for(
|
127
|
+
resource_name=logica_id, node_template=self._node_template
|
128
|
+
)
|
129
|
+
resource_unit = self.visit(node_resource)
|
130
|
+
before_context = resource_unit.before_context
|
131
|
+
after_context = resource_unit.after_context
|
132
|
+
return DescribeUnit(before_context=before_context, after_context=after_context)
|
133
|
+
|
134
|
+
def _resolve_mapping(self, map_name: str, top_level_key: str, second_level_key) -> DescribeUnit:
|
135
|
+
# TODO: add support for nested intrinsic functions, and KNOWN AFTER APPLY logical ids.
|
136
|
+
node_mapping: NodeMapping = self._get_node_mapping(map_name=map_name)
|
137
|
+
top_level_value = node_mapping.bindings.bindings.get(top_level_key)
|
138
|
+
if not isinstance(top_level_value, NodeObject):
|
139
|
+
raise RuntimeError()
|
140
|
+
second_level_value = top_level_value.bindings.get(second_level_key)
|
141
|
+
mapping_value_unit = self.visit(second_level_value)
|
142
|
+
return mapping_value_unit
|
111
143
|
|
112
144
|
def _resolve_reference_binding(
|
113
145
|
self, before_logical_id: str, after_logical_id: str
|
@@ -190,29 +222,37 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
190
222
|
arguments_unit = self.visit(node_intrinsic_function.arguments)
|
191
223
|
# TODO: validate the return value according to the spec.
|
192
224
|
before_argument_list = arguments_unit.before_context
|
193
|
-
|
194
|
-
before_attribute_name = before_argument_list[1]
|
195
|
-
before_node_resource = self._get_node_resource_for(
|
196
|
-
resource_name=before_logical_name_of_resource, node_template=self._node_template
|
197
|
-
)
|
198
|
-
node_property: TerminalValue = self._get_node_property_for(
|
199
|
-
property_name=before_attribute_name, node_resource=before_node_resource
|
200
|
-
)
|
225
|
+
after_argument_list = arguments_unit.after_context
|
201
226
|
|
202
|
-
before_context =
|
203
|
-
if
|
227
|
+
before_context = None
|
228
|
+
if before_argument_list:
|
229
|
+
before_logical_name_of_resource = before_argument_list[0]
|
230
|
+
before_attribute_name = before_argument_list[1]
|
231
|
+
before_node_resource = self._get_node_resource_for(
|
232
|
+
resource_name=before_logical_name_of_resource, node_template=self._node_template
|
233
|
+
)
|
234
|
+
before_node_property = self._get_node_property_for(
|
235
|
+
property_name=before_attribute_name, node_resource=before_node_resource
|
236
|
+
)
|
237
|
+
before_property_unit = self.visit(before_node_property)
|
238
|
+
before_context = before_property_unit.before_context
|
239
|
+
|
240
|
+
after_context = None
|
241
|
+
if after_argument_list:
|
204
242
|
after_context = CHANGESET_KNOWN_AFTER_APPLY
|
205
|
-
|
206
|
-
|
243
|
+
# TODO: the following is the logic to resolve the attribute in the `after` template
|
244
|
+
# this should be moved to the new base class and then be masked in this describer.
|
245
|
+
# after_logical_name_of_resource = after_argument_list[0]
|
246
|
+
# after_attribute_name = after_argument_list[1]
|
247
|
+
# after_node_resource = self._get_node_resource_for(
|
248
|
+
# resource_name=after_logical_name_of_resource, node_template=self._node_template
|
249
|
+
# )
|
250
|
+
# after_node_property = self._get_node_property_for(
|
251
|
+
# property_name=after_attribute_name, node_resource=after_node_resource
|
252
|
+
# )
|
253
|
+
# after_property_unit = self.visit(after_node_property)
|
254
|
+
# after_context = after_property_unit.after_context
|
207
255
|
|
208
|
-
match node_intrinsic_function.change_type:
|
209
|
-
case ChangeType.MODIFIED:
|
210
|
-
return DescribeUnit(before_context=before_context, after_context=after_context)
|
211
|
-
case ChangeType.CREATED:
|
212
|
-
return DescribeUnit(after_context=after_context)
|
213
|
-
case ChangeType.REMOVED:
|
214
|
-
return DescribeUnit(before_context=before_context)
|
215
|
-
# Unchanged
|
216
256
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
217
257
|
|
218
258
|
def visit_node_intrinsic_function_fn_equals(
|
@@ -281,8 +321,31 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
281
321
|
# Implicit change type computation.
|
282
322
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
283
323
|
|
324
|
+
def visit_node_intrinsic_function_fn_find_in_map(
|
325
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
326
|
+
) -> DescribeUnit:
|
327
|
+
# TODO: check for KNOWN AFTER APPLY values for logical ids coming from intrinsic functions as arguments.
|
328
|
+
# TODO: add type checking/validation for result unit?
|
329
|
+
arguments_unit = self.visit(node_intrinsic_function.arguments)
|
330
|
+
before_arguments = arguments_unit.before_context
|
331
|
+
after_arguments = arguments_unit.after_context
|
332
|
+
if before_arguments:
|
333
|
+
before_value_unit = self._resolve_mapping(*before_arguments)
|
334
|
+
before_context = before_value_unit.before_context
|
335
|
+
else:
|
336
|
+
before_context = None
|
337
|
+
if after_arguments:
|
338
|
+
after_value_unit = self._resolve_mapping(*after_arguments)
|
339
|
+
after_context = after_value_unit.after_context
|
340
|
+
else:
|
341
|
+
after_context = None
|
342
|
+
return DescribeUnit(before_context=before_context, after_context=after_context)
|
343
|
+
|
344
|
+
def visit_node_mapping(self, node_mapping: NodeMapping) -> DescribeUnit:
|
345
|
+
bindings_unit = self.visit(node_mapping.bindings)
|
346
|
+
return bindings_unit
|
347
|
+
|
284
348
|
def visit_node_parameter(self, node_parameter: NodeParameter) -> DescribeUnit:
|
285
|
-
# TODO: add caching for these operation, parameters may be referenced more than once.
|
286
349
|
# TODO: add support for default value sampling
|
287
350
|
dynamic_value = node_parameter.dynamic_value
|
288
351
|
describe_unit = self.visit(dynamic_value)
|
@@ -299,12 +362,16 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
299
362
|
|
300
363
|
# TODO: add tests with created and deleted parameters and verify this logic holds.
|
301
364
|
before_logical_id = arguments_unit.before_context
|
302
|
-
|
303
|
-
|
365
|
+
before_context = None
|
366
|
+
if before_logical_id is not None:
|
367
|
+
before_unit = self._resolve_reference(logica_id=before_logical_id)
|
368
|
+
before_context = before_unit.before_context
|
304
369
|
|
305
370
|
after_logical_id = arguments_unit.after_context
|
306
|
-
|
307
|
-
|
371
|
+
after_context = None
|
372
|
+
if after_logical_id is not None:
|
373
|
+
after_unit = self._resolve_reference(logica_id=after_logical_id)
|
374
|
+
after_context = after_unit.after_context
|
308
375
|
|
309
376
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
310
377
|
|
@@ -363,21 +430,71 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
363
430
|
)
|
364
431
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
365
432
|
|
433
|
+
def visit_node_output(self, node_output: NodeOutput) -> DescribeUnit:
|
434
|
+
# This logic is not required for Describe operations,
|
435
|
+
# and should be ported a new base for this class type.
|
436
|
+
change_type = node_output.change_type
|
437
|
+
value_unit = self.visit(node_output.value)
|
438
|
+
|
439
|
+
condition_unit = None
|
440
|
+
if node_output.condition_reference is not None:
|
441
|
+
condition_unit = self._resolve_resource_condition_reference(
|
442
|
+
node_output.condition_reference
|
443
|
+
)
|
444
|
+
condition_before = condition_unit.before_context
|
445
|
+
condition_after = condition_unit.after_context
|
446
|
+
if not condition_before and condition_after:
|
447
|
+
change_type = ChangeType.CREATED
|
448
|
+
elif condition_before and not condition_after:
|
449
|
+
change_type = ChangeType.REMOVED
|
450
|
+
|
451
|
+
export_unit = None
|
452
|
+
if node_output.export is not None:
|
453
|
+
export_unit = self.visit(node_output.export)
|
454
|
+
|
455
|
+
before_context = None
|
456
|
+
after_context = None
|
457
|
+
if change_type != ChangeType.REMOVED:
|
458
|
+
after_context = {"Name": node_output.name, ValueKey: value_unit.after_context}
|
459
|
+
if export_unit:
|
460
|
+
after_context[ExportKey] = export_unit.after_context
|
461
|
+
if condition_unit:
|
462
|
+
after_context[ConditionKey] = condition_unit.after_context
|
463
|
+
if change_type != ChangeType.CREATED:
|
464
|
+
before_context = {"Name": node_output.name, ValueKey: value_unit.before_context}
|
465
|
+
if export_unit:
|
466
|
+
before_context[ExportKey] = export_unit.before_context
|
467
|
+
if condition_unit:
|
468
|
+
before_context[ConditionKey] = condition_unit.before_context
|
469
|
+
return DescribeUnit(before_context=before_context, after_context=after_context)
|
470
|
+
|
471
|
+
def visit_node_outputs(self, node_outputs: NodeOutputs) -> DescribeUnit:
|
472
|
+
# This logic is not required for Describe operations,
|
473
|
+
# and should be ported a new base for this class type.
|
474
|
+
before_context = list()
|
475
|
+
after_context = list()
|
476
|
+
for node_output in node_outputs.outputs:
|
477
|
+
output_unit = self.visit(node_output)
|
478
|
+
output_before = output_unit.before_context
|
479
|
+
output_after = output_unit.after_context
|
480
|
+
if output_before:
|
481
|
+
before_context.append(output_before)
|
482
|
+
if output_after:
|
483
|
+
after_context.append(output_after)
|
484
|
+
return DescribeUnit(before_context=before_context, after_context=after_context)
|
485
|
+
|
366
486
|
def visit_node_resource(self, node_resource: NodeResource) -> DescribeUnit:
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
if change_type == ChangeType.UNCHANGED:
|
379
|
-
# TODO
|
380
|
-
return None
|
487
|
+
change_type = node_resource.change_type
|
488
|
+
if node_resource.condition_reference is not None:
|
489
|
+
condition_unit = self._resolve_resource_condition_reference(
|
490
|
+
node_resource.condition_reference
|
491
|
+
)
|
492
|
+
condition_before = condition_unit.before_context
|
493
|
+
condition_after = condition_unit.after_context
|
494
|
+
if not condition_before and condition_after:
|
495
|
+
change_type = ChangeType.CREATED
|
496
|
+
elif condition_before and not condition_after:
|
497
|
+
change_type = ChangeType.REMOVED
|
381
498
|
|
382
499
|
resource_change = cfn_api.ResourceChange()
|
383
500
|
resource_change["LogicalResourceId"] = node_resource.name
|
@@ -389,28 +506,28 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
389
506
|
)
|
390
507
|
|
391
508
|
properties_describe_unit = self.visit(node_resource.properties)
|
392
|
-
match change_type:
|
393
|
-
case ChangeType.MODIFIED:
|
394
|
-
resource_change["Action"] = cfn_api.ChangeAction.Modify
|
395
|
-
resource_change["BeforeContext"] = properties_describe_unit.before_context
|
396
|
-
resource_change["AfterContext"] = properties_describe_unit.after_context
|
397
|
-
case ChangeType.CREATED:
|
398
|
-
resource_change["Action"] = cfn_api.ChangeAction.Add
|
399
|
-
resource_change["AfterContext"] = properties_describe_unit.after_context
|
400
|
-
case ChangeType.REMOVED:
|
401
|
-
resource_change["Action"] = cfn_api.ChangeAction.Remove
|
402
|
-
resource_change["BeforeContext"] = properties_describe_unit.before_context
|
403
|
-
|
404
|
-
self._changes.append(
|
405
|
-
cfn_api.Change(Type=cfn_api.ChangeType.Resource, ResourceChange=resource_change)
|
406
|
-
)
|
407
509
|
|
408
|
-
|
409
|
-
|
510
|
+
if change_type != ChangeType.UNCHANGED:
|
511
|
+
match change_type:
|
512
|
+
case ChangeType.MODIFIED:
|
513
|
+
resource_change["Action"] = cfn_api.ChangeAction.Modify
|
514
|
+
resource_change["BeforeContext"] = properties_describe_unit.before_context
|
515
|
+
resource_change["AfterContext"] = properties_describe_unit.after_context
|
516
|
+
case ChangeType.CREATED:
|
517
|
+
resource_change["Action"] = cfn_api.ChangeAction.Add
|
518
|
+
resource_change["AfterContext"] = properties_describe_unit.after_context
|
519
|
+
case ChangeType.REMOVED:
|
520
|
+
resource_change["Action"] = cfn_api.ChangeAction.Remove
|
521
|
+
resource_change["BeforeContext"] = properties_describe_unit.before_context
|
522
|
+
self._changes.append(
|
523
|
+
cfn_api.Change(Type=cfn_api.ChangeType.Resource, ResourceChange=resource_change)
|
524
|
+
)
|
410
525
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
526
|
+
before_context = None
|
527
|
+
after_context = None
|
528
|
+
# TODO: reconsider what is the describe unit return value for a resource type.
|
529
|
+
if change_type != ChangeType.CREATED:
|
530
|
+
before_context = node_resource.name
|
531
|
+
if change_type != ChangeType.REMOVED:
|
532
|
+
after_context = node_resource.name
|
533
|
+
return DescribeUnit(before_context=before_context, after_context=after_context)
|
@@ -0,0 +1,170 @@
|
|
1
|
+
import logging
|
2
|
+
import uuid
|
3
|
+
from typing import Final
|
4
|
+
|
5
|
+
from localstack.aws.api.cloudformation import ChangeAction
|
6
|
+
from localstack.constants import INTERNAL_AWS_SECRET_ACCESS_KEY
|
7
|
+
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
8
|
+
NodeIntrinsicFunction,
|
9
|
+
NodeResource,
|
10
|
+
NodeTemplate,
|
11
|
+
TerminalValue,
|
12
|
+
)
|
13
|
+
from localstack.services.cloudformation.engine.v2.change_set_model_describer import (
|
14
|
+
ChangeSetModelDescriber,
|
15
|
+
DescribeUnit,
|
16
|
+
)
|
17
|
+
from localstack.services.cloudformation.resource_provider import (
|
18
|
+
Credentials,
|
19
|
+
OperationStatus,
|
20
|
+
ProgressEvent,
|
21
|
+
ResourceProviderExecutor,
|
22
|
+
ResourceProviderPayload,
|
23
|
+
get_resource_type,
|
24
|
+
)
|
25
|
+
|
26
|
+
LOG = logging.getLogger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
class ChangeSetModelExecutor(ChangeSetModelDescriber):
|
30
|
+
account_id: Final[str]
|
31
|
+
region: Final[str]
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self,
|
35
|
+
node_template: NodeTemplate,
|
36
|
+
account_id: str,
|
37
|
+
region: str,
|
38
|
+
stack_name: str,
|
39
|
+
stack_id: str,
|
40
|
+
):
|
41
|
+
super().__init__(node_template)
|
42
|
+
self.account_id = account_id
|
43
|
+
self.region = region
|
44
|
+
self.stack_name = stack_name
|
45
|
+
self.stack_id = stack_id
|
46
|
+
self.resources = {}
|
47
|
+
|
48
|
+
def execute(self) -> dict:
|
49
|
+
self.visit(self._node_template)
|
50
|
+
return self.resources
|
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
|
55
|
+
)
|
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}"
|
65
|
+
)
|
66
|
+
|
67
|
+
# TODO
|
68
|
+
resource_type = get_resource_type({"Type": "AWS::SSM::Parameter"})
|
69
|
+
payload = self.create_resource_provider_payload(
|
70
|
+
properties_describe_unit,
|
71
|
+
action,
|
72
|
+
node_resource.name,
|
73
|
+
resource_type,
|
74
|
+
)
|
75
|
+
resource_provider = resource_provider_executor.try_load_resource_provider(resource_type)
|
76
|
+
|
77
|
+
extra_resource_properties = {}
|
78
|
+
if resource_provider is not None:
|
79
|
+
# TODO: stack events
|
80
|
+
event = resource_provider_executor.deploy_loop(
|
81
|
+
resource_provider, extra_resource_properties, payload
|
82
|
+
)
|
83
|
+
else:
|
84
|
+
event = ProgressEvent(OperationStatus.SUCCESS, resource_model={})
|
85
|
+
|
86
|
+
self.resources.setdefault(node_resource.name, {"Properties": {}})
|
87
|
+
match event.status:
|
88
|
+
case OperationStatus.SUCCESS:
|
89
|
+
# merge the resources state with the external state
|
90
|
+
# 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)
|
93
|
+
# 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
|
96
|
+
case any:
|
97
|
+
raise NotImplementedError(f"Event status '{any}' not handled")
|
98
|
+
|
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
|
+
def create_resource_provider_payload(
|
136
|
+
self,
|
137
|
+
describe_unit: DescribeUnit,
|
138
|
+
action: ChangeAction,
|
139
|
+
logical_resource_id: str,
|
140
|
+
resource_type: str,
|
141
|
+
) -> ResourceProviderPayload:
|
142
|
+
# FIXME: use proper credentials
|
143
|
+
creds: Credentials = {
|
144
|
+
"accessKeyId": self.account_id,
|
145
|
+
"secretAccessKey": INTERNAL_AWS_SECRET_ACCESS_KEY,
|
146
|
+
"sessionToken": "",
|
147
|
+
}
|
148
|
+
resource_provider_payload: ResourceProviderPayload = {
|
149
|
+
"awsAccountId": self.account_id,
|
150
|
+
"callbackContext": {},
|
151
|
+
"stackId": self.stack_name,
|
152
|
+
"resourceType": resource_type,
|
153
|
+
"resourceTypeVersion": "000000",
|
154
|
+
# TODO: not actually a UUID
|
155
|
+
"bearerToken": str(uuid.uuid4()),
|
156
|
+
"region": self.region,
|
157
|
+
"action": str(action),
|
158
|
+
"requestData": {
|
159
|
+
"logicalResourceId": logical_resource_id,
|
160
|
+
"resourceProperties": describe_unit.after_context["Properties"],
|
161
|
+
"previousResourceProperties": describe_unit.before_context["Properties"],
|
162
|
+
"callerCredentials": creds,
|
163
|
+
"providerCredentials": creds,
|
164
|
+
"systemTags": {},
|
165
|
+
"previousSystemTags": {},
|
166
|
+
"stackTags": {},
|
167
|
+
"previousStackTags": {},
|
168
|
+
},
|
169
|
+
}
|
170
|
+
return resource_provider_payload
|
@@ -7,7 +7,11 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
7
7
|
NodeConditions,
|
8
8
|
NodeDivergence,
|
9
9
|
NodeIntrinsicFunction,
|
10
|
+
NodeMapping,
|
11
|
+
NodeMappings,
|
10
12
|
NodeObject,
|
13
|
+
NodeOutput,
|
14
|
+
NodeOutputs,
|
11
15
|
NodeParameter,
|
12
16
|
NodeParameters,
|
13
17
|
NodeProperties,
|
@@ -45,6 +49,18 @@ class ChangeSetModelVisitor(abc.ABC):
|
|
45
49
|
def visit_node_template(self, node_template: NodeTemplate):
|
46
50
|
self.visit_children(node_template)
|
47
51
|
|
52
|
+
def visit_node_mapping(self, node_mapping: NodeMapping):
|
53
|
+
self.visit_children(node_mapping)
|
54
|
+
|
55
|
+
def visit_node_mappings(self, node_mappings: NodeMappings):
|
56
|
+
self.visit_children(node_mappings)
|
57
|
+
|
58
|
+
def visit_node_outputs(self, node_outputs: NodeOutputs):
|
59
|
+
self.visit_children(node_outputs)
|
60
|
+
|
61
|
+
def visit_node_output(self, node_output: NodeOutput):
|
62
|
+
self.visit_children(node_output)
|
63
|
+
|
48
64
|
def visit_node_parameters(self, node_parameters: NodeParameters):
|
49
65
|
self.visit_children(node_parameters)
|
50
66
|
|
@@ -94,6 +110,11 @@ class ChangeSetModelVisitor(abc.ABC):
|
|
94
110
|
def visit_node_intrinsic_function_fn_not(self, node_intrinsic_function: NodeIntrinsicFunction):
|
95
111
|
self.visit_children(node_intrinsic_function)
|
96
112
|
|
113
|
+
def visit_node_intrinsic_function_fn_find_in_map(
|
114
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
115
|
+
):
|
116
|
+
self.visit_children(node_intrinsic_function)
|
117
|
+
|
97
118
|
def visit_node_intrinsic_function_ref(self, node_intrinsic_function: NodeIntrinsicFunction):
|
98
119
|
self.visit_children(node_intrinsic_function)
|
99
120
|
|