localstack-core 4.4.1.dev60__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 +106 -144
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +9 -7
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +7 -6
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +223 -114
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +10 -0
- localstack/services/s3/provider.py +3 -2
- localstack/services/sns/provider.py +27 -9
- localstack/version.py +2 -2
- {localstack_core-4.4.1.dev60.dist-info → localstack_core-4.4.1.dev65.dist-info}/METADATA +3 -3
- {localstack_core-4.4.1.dev60.dist-info → localstack_core-4.4.1.dev65.dist-info}/RECORD +20 -20
- localstack_core-4.4.1.dev65.dist-info/plux.json +1 -0
- localstack_core-4.4.1.dev60.dist-info/plux.json +0 -1
- {localstack_core-4.4.1.dev60.data → localstack_core-4.4.1.dev65.data}/scripts/localstack +0 -0
- {localstack_core-4.4.1.dev60.data → localstack_core-4.4.1.dev65.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.4.1.dev60.data → localstack_core-4.4.1.dev65.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.4.1.dev60.dist-info → localstack_core-4.4.1.dev65.dist-info}/WHEEL +0 -0
- {localstack_core-4.4.1.dev60.dist-info → localstack_core-4.4.1.dev65.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.4.1.dev60.dist-info → localstack_core-4.4.1.dev65.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.4.1.dev60.dist-info → localstack_core-4.4.1.dev65.dist-info}/top_level.txt +0 -0
@@ -3,9 +3,15 @@ from __future__ import annotations
|
|
3
3
|
import re
|
4
4
|
from typing import Any, Final, Generic, Optional, TypeVar
|
5
5
|
|
6
|
+
from localstack.services.cloudformation.engine.transformers import (
|
7
|
+
Transformer,
|
8
|
+
execute_macro,
|
9
|
+
transformers,
|
10
|
+
)
|
6
11
|
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
7
12
|
ChangeSetEntity,
|
8
13
|
ChangeType,
|
14
|
+
Maybe,
|
9
15
|
NodeArray,
|
10
16
|
NodeCondition,
|
11
17
|
NodeDependsOn,
|
@@ -20,16 +26,19 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
20
26
|
NodeProperty,
|
21
27
|
NodeResource,
|
22
28
|
NodeTemplate,
|
29
|
+
Nothing,
|
23
30
|
Scope,
|
24
31
|
TerminalValue,
|
25
32
|
TerminalValueCreated,
|
26
33
|
TerminalValueModified,
|
27
34
|
TerminalValueRemoved,
|
28
35
|
TerminalValueUnchanged,
|
36
|
+
is_nothing,
|
29
37
|
)
|
30
38
|
from localstack.services.cloudformation.engine.v2.change_set_model_visitor import (
|
31
39
|
ChangeSetModelVisitor,
|
32
40
|
)
|
41
|
+
from localstack.services.cloudformation.stores import get_cloudformation_store
|
33
42
|
from localstack.services.cloudformation.v2.entities import ChangeSet
|
34
43
|
from localstack.utils.aws.arns import get_partition
|
35
44
|
from localstack.utils.urls import localstack_host
|
@@ -52,10 +61,10 @@ TAfter = TypeVar("TAfter")
|
|
52
61
|
|
53
62
|
|
54
63
|
class PreprocEntityDelta(Generic[TBefore, TAfter]):
|
55
|
-
before:
|
56
|
-
after:
|
64
|
+
before: Maybe[TBefore]
|
65
|
+
after: Maybe[TAfter]
|
57
66
|
|
58
|
-
def __init__(self, before:
|
67
|
+
def __init__(self, before: Maybe[TBefore] = Nothing, after: Maybe[TAfter] = Nothing):
|
59
68
|
self.before = before
|
60
69
|
self.after = after
|
61
70
|
|
@@ -194,7 +203,6 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
194
203
|
_ = self._get_node_resource_for(
|
195
204
|
resource_name=resource_logical_id, node_template=self._node_template
|
196
205
|
)
|
197
|
-
|
198
206
|
resolved_resource = resolved_resources.get(resource_logical_id)
|
199
207
|
if resolved_resource is None:
|
200
208
|
raise RuntimeError(
|
@@ -231,26 +239,25 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
231
239
|
if mapping.name == map_name:
|
232
240
|
self.visit(mapping)
|
233
241
|
return mapping
|
234
|
-
|
235
|
-
raise RuntimeError()
|
242
|
+
raise RuntimeError(f"Undefined '{map_name}' mapping")
|
236
243
|
|
237
|
-
def _get_node_parameter_if_exists(self, parameter_name: str) ->
|
244
|
+
def _get_node_parameter_if_exists(self, parameter_name: str) -> Maybe[NodeParameter]:
|
238
245
|
parameters: list[NodeParameter] = self._node_template.parameters.parameters
|
239
246
|
# TODO: another scenarios suggesting property lookups might be preferable.
|
240
247
|
for parameter in parameters:
|
241
248
|
if parameter.name == parameter_name:
|
242
249
|
self.visit(parameter)
|
243
250
|
return parameter
|
244
|
-
return
|
251
|
+
return Nothing
|
245
252
|
|
246
|
-
def _get_node_condition_if_exists(self, condition_name: str) ->
|
253
|
+
def _get_node_condition_if_exists(self, condition_name: str) -> Maybe[NodeCondition]:
|
247
254
|
conditions: list[NodeCondition] = self._node_template.conditions.conditions
|
248
255
|
# TODO: another scenarios suggesting property lookups might be preferable.
|
249
256
|
for condition in conditions:
|
250
257
|
if condition.name == condition_name:
|
251
258
|
self.visit(condition)
|
252
259
|
return condition
|
253
|
-
return
|
260
|
+
return Nothing
|
254
261
|
|
255
262
|
def _resolve_condition(self, logical_id: str) -> PreprocEntityDelta:
|
256
263
|
node_condition = self._get_node_condition_if_exists(condition_name=logical_id)
|
@@ -274,14 +281,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
274
281
|
case "AWS::URLSuffix":
|
275
282
|
return _AWS_URL_SUFFIX
|
276
283
|
case "AWS::NoValue":
|
277
|
-
|
278
|
-
raise NotImplementedError("The use of AWS:NoValue is currently unsupported")
|
279
|
-
case "AWS::NotificationARNs":
|
280
|
-
raise NotImplementedError(
|
281
|
-
"The use of AWS::NotificationARNs is currently unsupported"
|
282
|
-
)
|
284
|
+
return None
|
283
285
|
case _:
|
284
|
-
raise RuntimeError(f"
|
286
|
+
raise RuntimeError(f"The use of '{pseudo_parameter_name}' is currently unsupported")
|
285
287
|
|
286
288
|
def _resolve_reference(self, logical_id: str) -> PreprocEntityDelta:
|
287
289
|
if logical_id in _PSEUDO_PARAMETERS:
|
@@ -317,11 +319,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
317
319
|
return mapping_value_delta
|
318
320
|
|
319
321
|
def visit(self, change_set_entity: ChangeSetEntity) -> PreprocEntityDelta:
|
320
|
-
|
321
|
-
if
|
322
|
+
scope = change_set_entity.scope
|
323
|
+
if scope in self._processed:
|
324
|
+
delta = self._processed[scope]
|
322
325
|
return delta
|
323
326
|
delta = super().visit(change_set_entity=change_set_entity)
|
324
|
-
self._processed[
|
327
|
+
self._processed[scope] = delta
|
325
328
|
return delta
|
326
329
|
|
327
330
|
def visit_terminal_value_modified(
|
@@ -356,21 +359,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
356
359
|
return PreprocEntityDelta(before=before_delta.before, after=after_delta.after)
|
357
360
|
|
358
361
|
def visit_node_object(self, node_object: NodeObject) -> PreprocEntityDelta:
|
359
|
-
|
360
|
-
|
362
|
+
node_change_type = node_object.change_type
|
363
|
+
before = dict() if node_change_type != ChangeType.CREATED else Nothing
|
364
|
+
after = dict() if node_change_type != ChangeType.REMOVED else Nothing
|
361
365
|
for name, change_set_entity in node_object.bindings.items():
|
362
366
|
delta: PreprocEntityDelta = self.visit(change_set_entity=change_set_entity)
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
case ChangeType.REMOVED:
|
370
|
-
before[name] = delta.before
|
371
|
-
case ChangeType.UNCHANGED:
|
372
|
-
before[name] = delta.before
|
373
|
-
after[name] = delta.before
|
367
|
+
delta_before = delta.before
|
368
|
+
delta_after = delta.after
|
369
|
+
if not is_nothing(before) and not is_nothing(delta_before) and delta_before is not None:
|
370
|
+
before[name] = delta_before
|
371
|
+
if not is_nothing(after) and not is_nothing(delta_after) and delta_after is not None:
|
372
|
+
after[name] = delta_after
|
374
373
|
return PreprocEntityDelta(before=before, after=after)
|
375
374
|
|
376
375
|
def visit_node_intrinsic_function_fn_get_att(
|
@@ -378,14 +377,14 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
378
377
|
) -> PreprocEntityDelta:
|
379
378
|
# TODO: validate the return value according to the spec.
|
380
379
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
381
|
-
before_argument:
|
380
|
+
before_argument: Maybe[list[str]] = arguments_delta.before
|
382
381
|
if isinstance(before_argument, str):
|
383
382
|
before_argument = before_argument.split(".")
|
384
|
-
after_argument:
|
383
|
+
after_argument: Maybe[list[str]] = arguments_delta.after
|
385
384
|
if isinstance(after_argument, str):
|
386
385
|
after_argument = after_argument.split(".")
|
387
386
|
|
388
|
-
before =
|
387
|
+
before = Nothing
|
389
388
|
if before_argument:
|
390
389
|
before_logical_name_of_resource = before_argument[0]
|
391
390
|
before_attribute_name = before_argument[1]
|
@@ -408,7 +407,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
408
407
|
property_name=before_attribute_name,
|
409
408
|
)
|
410
409
|
|
411
|
-
after =
|
410
|
+
after = Nothing
|
412
411
|
if after_argument:
|
413
412
|
after_logical_name_of_resource = after_argument[0]
|
414
413
|
after_attribute_name = after_argument[1]
|
@@ -438,10 +437,10 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
438
437
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
439
438
|
before_values = arguments_delta.before
|
440
439
|
after_values = arguments_delta.after
|
441
|
-
before =
|
440
|
+
before = Nothing
|
442
441
|
if before_values:
|
443
442
|
before = before_values[0] == before_values[1]
|
444
|
-
after =
|
443
|
+
after = Nothing
|
445
444
|
if after_values:
|
446
445
|
after = after_values[0] == after_values[1]
|
447
446
|
return PreprocEntityDelta(before=before, after=after)
|
@@ -450,6 +449,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
450
449
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
451
450
|
) -> PreprocEntityDelta:
|
452
451
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
452
|
+
arguments_before = arguments_delta.before
|
453
|
+
arguments_after = arguments_delta.after
|
453
454
|
|
454
455
|
def _compute_delta_for_if_statement(args: list[Any]) -> PreprocEntityDelta:
|
455
456
|
condition_name = args[0]
|
@@ -460,13 +461,13 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
460
461
|
)
|
461
462
|
|
462
463
|
# TODO: add support for this being created or removed.
|
463
|
-
before =
|
464
|
-
if
|
465
|
-
before_outcome_delta = _compute_delta_for_if_statement(
|
464
|
+
before = Nothing
|
465
|
+
if not is_nothing(arguments_before):
|
466
|
+
before_outcome_delta = _compute_delta_for_if_statement(arguments_before)
|
466
467
|
before = before_outcome_delta.before
|
467
|
-
after =
|
468
|
-
if
|
469
|
-
after_outcome_delta = _compute_delta_for_if_statement(
|
468
|
+
after = Nothing
|
469
|
+
if not is_nothing(arguments_after):
|
470
|
+
after_outcome_delta = _compute_delta_for_if_statement(arguments_after)
|
470
471
|
after = after_outcome_delta.after
|
471
472
|
return PreprocEntityDelta(before=before, after=after)
|
472
473
|
|
@@ -476,20 +477,89 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
476
477
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
477
478
|
before_condition = arguments_delta.before
|
478
479
|
after_condition = arguments_delta.after
|
479
|
-
|
480
|
+
before = Nothing
|
481
|
+
if not is_nothing(before_condition):
|
480
482
|
before_condition_outcome = before_condition[0]
|
481
483
|
before = not before_condition_outcome
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
if after_condition:
|
484
|
+
after = Nothing
|
485
|
+
if not is_nothing(after_condition):
|
486
486
|
after_condition_outcome = after_condition[0]
|
487
487
|
after = not after_condition_outcome
|
488
|
-
else:
|
489
|
-
after = None
|
490
488
|
# Implicit change type computation.
|
491
489
|
return PreprocEntityDelta(before=before, after=after)
|
492
490
|
|
491
|
+
def _compute_fn_transform(self, args: dict[str, Any]) -> Any:
|
492
|
+
# TODO: add typing to arguments before this level.
|
493
|
+
# TODO: add schema validation
|
494
|
+
# TODO: add support for other transform types
|
495
|
+
|
496
|
+
account_id = self._change_set.account_id
|
497
|
+
region_name = self._change_set.region_name
|
498
|
+
transform_name: str = args.get("Name")
|
499
|
+
if not isinstance(transform_name, str):
|
500
|
+
raise RuntimeError("Invalid or missing Fn::Transform 'Name' argument")
|
501
|
+
transform_parameters: dict = args.get("Parameters")
|
502
|
+
if not isinstance(transform_parameters, dict):
|
503
|
+
raise RuntimeError("Invalid or missing Fn::Transform 'Parameters' argument")
|
504
|
+
|
505
|
+
if transform_name in transformers:
|
506
|
+
# TODO: port and refactor this 'transformers' logic to this package.
|
507
|
+
builtin_transformer_class = transformers[transform_name]
|
508
|
+
builtin_transformer: Transformer = builtin_transformer_class()
|
509
|
+
transform_output: Any = builtin_transformer.transform(
|
510
|
+
account_id=account_id, region_name=region_name, parameters=transform_parameters
|
511
|
+
)
|
512
|
+
return transform_output
|
513
|
+
|
514
|
+
macros_store = get_cloudformation_store(
|
515
|
+
account_id=account_id, region_name=region_name
|
516
|
+
).macros
|
517
|
+
if transform_name in macros_store:
|
518
|
+
# TODO: this formatting of stack parameters is odd but required to integrate with v1 execute_macro util.
|
519
|
+
# consider porting this utils and passing the plain list of parameters instead.
|
520
|
+
stack_parameters = {
|
521
|
+
parameter["ParameterKey"]: parameter
|
522
|
+
for parameter in self._change_set.stack.parameters
|
523
|
+
}
|
524
|
+
transform_output: Any = execute_macro(
|
525
|
+
account_id=account_id,
|
526
|
+
region_name=region_name,
|
527
|
+
parsed_template=dict(), # TODO: review the requirements for this argument.
|
528
|
+
macro=args, # TODO: review support for non dict bindings (v1).
|
529
|
+
stack_parameters=stack_parameters,
|
530
|
+
transformation_parameters=transform_parameters,
|
531
|
+
is_intrinsic=True,
|
532
|
+
)
|
533
|
+
return transform_output
|
534
|
+
|
535
|
+
raise RuntimeError(
|
536
|
+
f"Unsupported transform function '{transform_name}' in '{self._change_set.stack.stack_name}'"
|
537
|
+
)
|
538
|
+
|
539
|
+
def visit_node_intrinsic_function_fn_transform(
|
540
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
541
|
+
) -> PreprocEntityDelta:
|
542
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
543
|
+
arguments_before = arguments_delta.before
|
544
|
+
arguments_after = arguments_delta.after
|
545
|
+
|
546
|
+
# TODO: review the use of cache in self.precessed from the 'before' run to
|
547
|
+
# ensure changes to the lambda (such as after UpdateFunctionCode) do not
|
548
|
+
# generalise tot he before value at this depth (thus making it seems as
|
549
|
+
# though for this transformation before==after). Another options may be to
|
550
|
+
# have specialised caching for transformations.
|
551
|
+
|
552
|
+
# TODO: add tests to review the behaviour of CFN with changes to transformation
|
553
|
+
# function code and no changes to the template.
|
554
|
+
|
555
|
+
before = Nothing
|
556
|
+
if not is_nothing(arguments_before):
|
557
|
+
before = self._compute_fn_transform(args=arguments_before)
|
558
|
+
after = Nothing
|
559
|
+
if not is_nothing(arguments_after):
|
560
|
+
after = self._compute_fn_transform(args=arguments_after)
|
561
|
+
return PreprocEntityDelta(before=before, after=after)
|
562
|
+
|
493
563
|
def visit_node_intrinsic_function_fn_sub(
|
494
564
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
495
565
|
) -> PreprocEntityDelta:
|
@@ -528,10 +598,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
528
598
|
template_variable_value = sub_parameters[template_variable_name]
|
529
599
|
else:
|
530
600
|
try:
|
531
|
-
|
601
|
+
resource_delta = self._resolve_reference(logical_id=template_variable_name)
|
532
602
|
template_variable_value = (
|
533
|
-
|
603
|
+
resource_delta.before if select_before else resource_delta.after
|
534
604
|
)
|
605
|
+
if isinstance(template_variable_value, PreprocResource):
|
606
|
+
template_variable_value = template_variable_value.logical_id
|
535
607
|
except RuntimeError:
|
536
608
|
raise RuntimeError(
|
537
609
|
f"Undefined variable name in Fn::Sub string template '{template_variable_name}'"
|
@@ -541,19 +613,11 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
541
613
|
)
|
542
614
|
return sub_string
|
543
615
|
|
544
|
-
before =
|
545
|
-
if (
|
546
|
-
isinstance(arguments_before, str)
|
547
|
-
or isinstance(arguments_before, list)
|
548
|
-
and len(arguments_before) == 2
|
549
|
-
):
|
616
|
+
before = Nothing
|
617
|
+
if not is_nothing(arguments_before):
|
550
618
|
before = _compute_sub(args=arguments_before, select_before=True)
|
551
|
-
after =
|
552
|
-
if (
|
553
|
-
isinstance(arguments_after, str)
|
554
|
-
or isinstance(arguments_after, list)
|
555
|
-
and len(arguments_after) == 2
|
556
|
-
):
|
619
|
+
after = Nothing
|
620
|
+
if not is_nothing(arguments_after):
|
557
621
|
after = _compute_sub(args=arguments_after)
|
558
622
|
return PreprocEntityDelta(before=before, after=after)
|
559
623
|
|
@@ -574,14 +638,43 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
574
638
|
join_result = delimiter.join(map(str, values))
|
575
639
|
return join_result
|
576
640
|
|
577
|
-
before =
|
641
|
+
before = Nothing
|
578
642
|
if isinstance(arguments_before, list) and len(arguments_before) == 2:
|
579
643
|
before = _compute_join(arguments_before)
|
580
|
-
after =
|
644
|
+
after = Nothing
|
581
645
|
if isinstance(arguments_after, list) and len(arguments_after) == 2:
|
582
646
|
after = _compute_join(arguments_after)
|
583
647
|
return PreprocEntityDelta(before=before, after=after)
|
584
648
|
|
649
|
+
def visit_node_intrinsic_function_fn_select(
|
650
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
651
|
+
):
|
652
|
+
# TODO: add further support for schema validation
|
653
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
654
|
+
arguments_before = arguments_delta.before
|
655
|
+
arguments_after = arguments_delta.after
|
656
|
+
|
657
|
+
def _compute_fn_select(args: list[Any]) -> Any:
|
658
|
+
values: list[Any] = args[1]
|
659
|
+
if not isinstance(values, list) or not values:
|
660
|
+
raise RuntimeError(f"Invalid arguments list value for Fn::Select: '{values}'")
|
661
|
+
values_len = len(values)
|
662
|
+
index: int = int(args[0])
|
663
|
+
if not isinstance(index, int) or index < 0 or index > values_len:
|
664
|
+
raise RuntimeError(f"Invalid or out of range index value for Fn::Select: '{index}'")
|
665
|
+
selection = values[index]
|
666
|
+
return selection
|
667
|
+
|
668
|
+
before = Nothing
|
669
|
+
if not is_nothing(arguments_before):
|
670
|
+
before = _compute_fn_select(arguments_before)
|
671
|
+
|
672
|
+
after = Nothing
|
673
|
+
if not is_nothing(arguments_after):
|
674
|
+
after = _compute_fn_select(arguments_after)
|
675
|
+
|
676
|
+
return PreprocEntityDelta(before=before, after=after)
|
677
|
+
|
585
678
|
def visit_node_intrinsic_function_fn_find_in_map(
|
586
679
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
587
680
|
) -> PreprocEntityDelta:
|
@@ -589,16 +682,14 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
589
682
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
590
683
|
before_arguments = arguments_delta.before
|
591
684
|
after_arguments = arguments_delta.after
|
685
|
+
before = Nothing
|
592
686
|
if before_arguments:
|
593
687
|
before_value_delta = self._resolve_mapping(*before_arguments)
|
594
688
|
before = before_value_delta.before
|
595
|
-
|
596
|
-
before = None
|
689
|
+
after = Nothing
|
597
690
|
if after_arguments:
|
598
691
|
after_value_delta = self._resolve_mapping(*after_arguments)
|
599
692
|
after = after_value_delta.after
|
600
|
-
else:
|
601
|
-
after = None
|
602
693
|
return PreprocEntityDelta(before=before, after=after)
|
603
694
|
|
604
695
|
def visit_node_mapping(self, node_mapping: NodeMapping) -> PreprocEntityDelta:
|
@@ -653,15 +744,15 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
653
744
|
after_logical_id = arguments_delta.after
|
654
745
|
|
655
746
|
# TODO: extend this to support references to other types.
|
656
|
-
before =
|
657
|
-
if
|
747
|
+
before = Nothing
|
748
|
+
if not is_nothing(before_logical_id):
|
658
749
|
before_delta = self._resolve_reference(logical_id=before_logical_id)
|
659
750
|
before = before_delta.before
|
660
751
|
if isinstance(before, PreprocResource):
|
661
752
|
before = before.physical_resource_id
|
662
753
|
|
663
|
-
after =
|
664
|
-
if
|
754
|
+
after = Nothing
|
755
|
+
if not is_nothing(after_logical_id):
|
665
756
|
after_delta = self._resolve_reference(logical_id=after_logical_id)
|
666
757
|
after = after_delta.after
|
667
758
|
if isinstance(after, PreprocResource):
|
@@ -670,14 +761,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
670
761
|
return PreprocEntityDelta(before=before, after=after)
|
671
762
|
|
672
763
|
def visit_node_array(self, node_array: NodeArray) -> PreprocEntityDelta:
|
673
|
-
|
674
|
-
|
764
|
+
node_change_type = node_array.change_type
|
765
|
+
before = list() if node_change_type != ChangeType.CREATED else Nothing
|
766
|
+
after = list() if node_change_type != ChangeType.REMOVED else Nothing
|
675
767
|
for change_set_entity in node_array.array:
|
676
768
|
delta: PreprocEntityDelta = self.visit(change_set_entity=change_set_entity)
|
677
|
-
|
678
|
-
|
679
|
-
if
|
680
|
-
|
769
|
+
delta_before = delta.before
|
770
|
+
delta_after = delta.after
|
771
|
+
if not is_nothing(before) and not is_nothing(delta_before):
|
772
|
+
before.append(delta_before)
|
773
|
+
if not is_nothing(after) and not is_nothing(delta_after):
|
774
|
+
after.append(delta_after)
|
681
775
|
return PreprocEntityDelta(before=before, after=after)
|
682
776
|
|
683
777
|
def visit_node_property(self, node_property: NodeProperty) -> PreprocEntityDelta:
|
@@ -686,29 +780,44 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
686
780
|
def visit_node_properties(
|
687
781
|
self, node_properties: NodeProperties
|
688
782
|
) -> PreprocEntityDelta[PreprocProperties, PreprocProperties]:
|
689
|
-
|
690
|
-
|
783
|
+
node_change_type = node_properties.change_type
|
784
|
+
before_bindings = dict() if node_change_type != ChangeType.CREATED else Nothing
|
785
|
+
after_bindings = dict() if node_change_type != ChangeType.REMOVED else Nothing
|
691
786
|
for node_property in node_properties.properties:
|
692
|
-
delta = self.visit(node_property)
|
693
787
|
property_name = node_property.name
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
788
|
+
delta = self.visit(node_property)
|
789
|
+
delta_before = delta.before
|
790
|
+
delta_after = delta.after
|
791
|
+
if (
|
792
|
+
not is_nothing(before_bindings)
|
793
|
+
and not is_nothing(delta_before)
|
794
|
+
and delta_before is not None
|
795
|
+
):
|
796
|
+
before_bindings[property_name] = delta_before
|
797
|
+
if (
|
798
|
+
not is_nothing(after_bindings)
|
799
|
+
and not is_nothing(delta_after)
|
800
|
+
and delta_after is not None
|
801
|
+
):
|
802
|
+
after_bindings[property_name] = delta_after
|
803
|
+
before = Nothing
|
804
|
+
if not is_nothing(before_bindings):
|
805
|
+
before = PreprocProperties(properties=before_bindings)
|
806
|
+
after = Nothing
|
807
|
+
if not is_nothing(after_bindings):
|
808
|
+
after = PreprocProperties(properties=after_bindings)
|
700
809
|
return PreprocEntityDelta(before=before, after=after)
|
701
810
|
|
702
811
|
def _resolve_resource_condition_reference(self, reference: TerminalValue) -> PreprocEntityDelta:
|
703
812
|
reference_delta = self.visit(reference)
|
704
813
|
before_reference = reference_delta.before
|
705
|
-
before =
|
706
|
-
if before_reference
|
814
|
+
before = Nothing
|
815
|
+
if isinstance(before_reference, str):
|
707
816
|
before_delta = self._resolve_condition(logical_id=before_reference)
|
708
817
|
before = before_delta.before
|
709
|
-
after =
|
818
|
+
after = Nothing
|
710
819
|
after_reference = reference_delta.after
|
711
|
-
if after_reference
|
820
|
+
if isinstance(after_reference, str):
|
712
821
|
after_delta = self._resolve_condition(logical_id=after_reference)
|
713
822
|
after = after_delta.after
|
714
823
|
return PreprocEntityDelta(before=before, after=after)
|
@@ -717,19 +826,19 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
717
826
|
self, node_resource: NodeResource
|
718
827
|
) -> PreprocEntityDelta[PreprocResource, PreprocResource]:
|
719
828
|
change_type = node_resource.change_type
|
720
|
-
condition_before =
|
721
|
-
condition_after =
|
722
|
-
if node_resource.condition_reference
|
829
|
+
condition_before = Nothing
|
830
|
+
condition_after = Nothing
|
831
|
+
if not is_nothing(node_resource.condition_reference):
|
723
832
|
condition_delta = self._resolve_resource_condition_reference(
|
724
833
|
node_resource.condition_reference
|
725
834
|
)
|
726
835
|
condition_before = condition_delta.before
|
727
836
|
condition_after = condition_delta.after
|
728
837
|
|
729
|
-
depends_on_before =
|
730
|
-
depends_on_after =
|
731
|
-
if node_resource.depends_on
|
732
|
-
depends_on_delta = self.
|
838
|
+
depends_on_before = Nothing
|
839
|
+
depends_on_after = Nothing
|
840
|
+
if not is_nothing(node_resource.depends_on):
|
841
|
+
depends_on_delta = self.visit(node_resource.depends_on)
|
733
842
|
depends_on_before = depends_on_delta.before
|
734
843
|
depends_on_after = depends_on_delta.after
|
735
844
|
|
@@ -738,9 +847,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
738
847
|
node_resource.properties
|
739
848
|
)
|
740
849
|
|
741
|
-
before =
|
742
|
-
after =
|
743
|
-
if change_type != ChangeType.CREATED and condition_before
|
850
|
+
before = Nothing
|
851
|
+
after = Nothing
|
852
|
+
if change_type != ChangeType.CREATED and is_nothing(condition_before) or condition_before:
|
744
853
|
logical_resource_id = node_resource.name
|
745
854
|
before_physical_resource_id = self._before_resource_physical_id(
|
746
855
|
resource_logical_id=logical_resource_id
|
@@ -753,7 +862,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
753
862
|
properties=properties_delta.before,
|
754
863
|
depends_on=depends_on_before,
|
755
864
|
)
|
756
|
-
if change_type != ChangeType.REMOVED and condition_after
|
865
|
+
if change_type != ChangeType.REMOVED and is_nothing(condition_after) or condition_after:
|
757
866
|
logical_resource_id = node_resource.name
|
758
867
|
try:
|
759
868
|
after_physical_resource_id = self._after_resource_physical_id(
|
@@ -777,8 +886,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
777
886
|
change_type = node_output.change_type
|
778
887
|
value_delta = self.visit(node_output.value)
|
779
888
|
|
780
|
-
condition_delta =
|
781
|
-
if node_output.condition_reference
|
889
|
+
condition_delta = Nothing
|
890
|
+
if not is_nothing(node_output.condition_reference):
|
782
891
|
condition_delta = self._resolve_resource_condition_reference(
|
783
892
|
node_output.condition_reference
|
784
893
|
)
|
@@ -789,11 +898,11 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
789
898
|
elif condition_before and not condition_after:
|
790
899
|
change_type = ChangeType.REMOVED
|
791
900
|
|
792
|
-
export_delta =
|
793
|
-
if node_output.export
|
901
|
+
export_delta = Nothing
|
902
|
+
if not is_nothing(node_output.export):
|
794
903
|
export_delta = self.visit(node_output.export)
|
795
904
|
|
796
|
-
before:
|
905
|
+
before: Maybe[PreprocOutput] = Nothing
|
797
906
|
if change_type != ChangeType.CREATED:
|
798
907
|
before = PreprocOutput(
|
799
908
|
name=node_output.name,
|
@@ -801,7 +910,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
801
910
|
export=export_delta.before if export_delta else None,
|
802
911
|
condition=condition_delta.before if condition_delta else None,
|
803
912
|
)
|
804
|
-
after:
|
913
|
+
after: Maybe[PreprocOutput] = Nothing
|
805
914
|
if change_type != ChangeType.REMOVED:
|
806
915
|
after = PreprocOutput(
|
807
916
|
name=node_output.name,
|
@@ -820,8 +929,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
820
929
|
output_delta: PreprocEntityDelta[PreprocOutput, PreprocOutput] = self.visit(node_output)
|
821
930
|
output_before = output_delta.before
|
822
931
|
output_after = output_delta.after
|
823
|
-
if output_before:
|
932
|
+
if not is_nothing(output_before):
|
824
933
|
before.append(output_before)
|
825
|
-
if output_after:
|
934
|
+
if not is_nothing(output_after):
|
826
935
|
after.append(output_after)
|
827
936
|
return PreprocEntityDelta(before=before, after=after)
|
@@ -113,6 +113,16 @@ class ChangeSetModelVisitor(abc.ABC):
|
|
113
113
|
):
|
114
114
|
self.visit_children(node_intrinsic_function)
|
115
115
|
|
116
|
+
def visit_node_intrinsic_function_fn_transform(
|
117
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
118
|
+
):
|
119
|
+
self.visit_children(node_intrinsic_function)
|
120
|
+
|
121
|
+
def visit_node_intrinsic_function_fn_select(
|
122
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
123
|
+
):
|
124
|
+
self.visit_children(node_intrinsic_function)
|
125
|
+
|
116
126
|
def visit_node_intrinsic_function_fn_sub(self, node_intrinsic_function: NodeIntrinsicFunction):
|
117
127
|
self.visit_children(node_intrinsic_function)
|
118
128
|
|
@@ -3815,8 +3815,9 @@ class S3Provider(S3Api, ServiceLifecycleHook):
|
|
3815
3815
|
context: RequestContext,
|
3816
3816
|
bucket: BucketName,
|
3817
3817
|
ownership_controls: OwnershipControls,
|
3818
|
-
content_md5: ContentMD5 = None,
|
3819
|
-
expected_bucket_owner: AccountId = None,
|
3818
|
+
content_md5: ContentMD5 | None = None,
|
3819
|
+
expected_bucket_owner: AccountId | None = None,
|
3820
|
+
checksum_algorithm: ChecksumAlgorithm | None = None,
|
3820
3821
|
**kwargs,
|
3821
3822
|
) -> None:
|
3822
3823
|
# TODO: this currently only mock the operation, but its actual effect is not emulated
|