localstack-core 4.3.1.dev6__py3-none-any.whl → 4.3.1.dev28__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 +597 -0
- localstack/aws/api/events/__init__.py +18 -12
- localstack/aws/api/route53/__init__.py +2 -0
- localstack/aws/api/s3control/__init__.py +89 -0
- localstack/aws/api/transcribe/__init__.py +1 -0
- 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 +164 -35
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +143 -69
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +170 -0
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +8 -0
- localstack/services/cloudformation/v2/provider.py +72 -6
- localstack/services/ec2/patches.py +31 -3
- localstack/services/events/provider.py +6 -1
- 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.dev6.dist-info → localstack_core-4.3.1.dev28.dist-info}/METADATA +5 -5
- {localstack_core-4.3.1.dev6.dist-info → localstack_core-4.3.1.dev28.dist-info}/RECORD +43 -42
- localstack_core-4.3.1.dev28.dist-info/plux.json +1 -0
- localstack_core-4.3.1.dev6.dist-info/plux.json +0 -1
- {localstack_core-4.3.1.dev6.data → localstack_core-4.3.1.dev28.data}/scripts/localstack +0 -0
- {localstack_core-4.3.1.dev6.data → localstack_core-4.3.1.dev28.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.3.1.dev6.data → localstack_core-4.3.1.dev28.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.3.1.dev6.dist-info → localstack_core-4.3.1.dev28.dist-info}/WHEEL +0 -0
- {localstack_core-4.3.1.dev6.dist-info → localstack_core-4.3.1.dev28.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.3.1.dev6.dist-info → localstack_core-4.3.1.dev28.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.3.1.dev6.dist-info → localstack_core-4.3.1.dev28.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@ from typing import Any, Final, Generator, Optional, Union, cast
|
|
7
7
|
|
8
8
|
from typing_extensions import TypeVar
|
9
9
|
|
10
|
+
from localstack.aws.api.cloudformation import ChangeAction
|
10
11
|
from localstack.utils.strings import camel_to_snake_case
|
11
12
|
|
12
13
|
T = TypeVar("T")
|
@@ -66,6 +67,15 @@ class ChangeType(enum.Enum):
|
|
66
67
|
def __str__(self):
|
67
68
|
return self.value
|
68
69
|
|
70
|
+
def to_action(self) -> ChangeAction | None:
|
71
|
+
match self:
|
72
|
+
case self.CREATED:
|
73
|
+
return ChangeAction.Add
|
74
|
+
case self.MODIFIED:
|
75
|
+
return ChangeAction.Modify
|
76
|
+
case self.REMOVED:
|
77
|
+
return ChangeAction.Remove
|
78
|
+
|
69
79
|
def for_child(self, child_change_type: ChangeType) -> ChangeType:
|
70
80
|
if child_change_type == self:
|
71
81
|
return self
|
@@ -117,6 +127,7 @@ class NodeTemplate(ChangeSetNode):
|
|
117
127
|
parameters: Final[NodeParameters]
|
118
128
|
conditions: Final[NodeConditions]
|
119
129
|
resources: Final[NodeResources]
|
130
|
+
outputs: Final[NodeOutputs]
|
120
131
|
|
121
132
|
def __init__(
|
122
133
|
self,
|
@@ -126,12 +137,14 @@ class NodeTemplate(ChangeSetNode):
|
|
126
137
|
parameters: NodeParameters,
|
127
138
|
conditions: NodeConditions,
|
128
139
|
resources: NodeResources,
|
140
|
+
outputs: NodeOutputs,
|
129
141
|
):
|
130
142
|
super().__init__(scope=scope, change_type=change_type)
|
131
143
|
self.mappings = mappings
|
132
144
|
self.parameters = parameters
|
133
145
|
self.conditions = conditions
|
134
146
|
self.resources = resources
|
147
|
+
self.outputs = outputs
|
135
148
|
|
136
149
|
|
137
150
|
class NodeDivergence(ChangeSetNode):
|
@@ -189,6 +202,36 @@ class NodeMappings(ChangeSetNode):
|
|
189
202
|
self.mappings = mappings
|
190
203
|
|
191
204
|
|
205
|
+
class NodeOutput(ChangeSetNode):
|
206
|
+
name: Final[str]
|
207
|
+
value: Final[ChangeSetEntity]
|
208
|
+
export: Final[Optional[ChangeSetEntity]]
|
209
|
+
condition_reference: Final[Optional[TerminalValue]]
|
210
|
+
|
211
|
+
def __init__(
|
212
|
+
self,
|
213
|
+
scope: Scope,
|
214
|
+
change_type: ChangeType,
|
215
|
+
name: str,
|
216
|
+
value: ChangeSetEntity,
|
217
|
+
export: Optional[ChangeSetEntity],
|
218
|
+
conditional_reference: Optional[TerminalValue],
|
219
|
+
):
|
220
|
+
super().__init__(scope=scope, change_type=change_type)
|
221
|
+
self.name = name
|
222
|
+
self.value = value
|
223
|
+
self.export = export
|
224
|
+
self.condition_reference = conditional_reference
|
225
|
+
|
226
|
+
|
227
|
+
class NodeOutputs(ChangeSetNode):
|
228
|
+
outputs: Final[list[NodeOutput]]
|
229
|
+
|
230
|
+
def __init__(self, scope: Scope, change_type: ChangeType, outputs: list[NodeOutput]):
|
231
|
+
super().__init__(scope=scope, change_type=change_type)
|
232
|
+
self.outputs = outputs
|
233
|
+
|
234
|
+
|
192
235
|
class NodeCondition(ChangeSetNode):
|
193
236
|
name: Final[str]
|
194
237
|
body: Final[ChangeSetEntity]
|
@@ -218,7 +261,7 @@ class NodeResources(ChangeSetNode):
|
|
218
261
|
class NodeResource(ChangeSetNode):
|
219
262
|
name: Final[str]
|
220
263
|
type_: Final[ChangeSetTerminal]
|
221
|
-
condition_reference: Final[TerminalValue]
|
264
|
+
condition_reference: Final[Optional[TerminalValue]]
|
222
265
|
properties: Final[NodeProperties]
|
223
266
|
|
224
267
|
def __init__(
|
@@ -325,6 +368,9 @@ MappingsKey: Final[str] = "Mappings"
|
|
325
368
|
ResourcesKey: Final[str] = "Resources"
|
326
369
|
PropertiesKey: Final[str] = "Properties"
|
327
370
|
ParametersKey: Final[str] = "Parameters"
|
371
|
+
ValueKey: Final[str] = "Value"
|
372
|
+
ExportKey: Final[str] = "Export"
|
373
|
+
OutputsKey: Final[str] = "Outputs"
|
328
374
|
# TODO: expand intrinsic functions set.
|
329
375
|
RefKey: Final[str] = "Ref"
|
330
376
|
FnIf: Final[str] = "Fn::If"
|
@@ -567,17 +613,9 @@ class ChangeSetModel:
|
|
567
613
|
binding_scope, (before_value, after_value) = self._safe_access_in(
|
568
614
|
scope, binding_name, before_object, after_object
|
569
615
|
)
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
intrinsic_function=binding_name,
|
574
|
-
before_arguments=before_value,
|
575
|
-
after_arguments=after_value,
|
576
|
-
)
|
577
|
-
else:
|
578
|
-
value = self._visit_value(
|
579
|
-
scope=binding_scope, before_value=before_value, after_value=after_value
|
580
|
-
)
|
616
|
+
value = self._visit_value(
|
617
|
+
scope=binding_scope, before_value=before_value, after_value=after_value
|
618
|
+
)
|
581
619
|
bindings[binding_name] = value
|
582
620
|
change_type = change_type.for_child(value.change_type)
|
583
621
|
node_object = NodeObject(scope=scope, change_type=change_type, bindings=bindings)
|
@@ -601,8 +639,11 @@ class ChangeSetModel:
|
|
601
639
|
value = self._visited_scopes.get(scope)
|
602
640
|
if isinstance(value, ChangeSetEntity):
|
603
641
|
return value
|
642
|
+
|
643
|
+
before_type_name = self._type_name_of(before_value)
|
644
|
+
after_type_name = self._type_name_of(after_value)
|
604
645
|
unset = object()
|
605
|
-
if
|
646
|
+
if before_type_name == after_type_name:
|
606
647
|
dominant_value = before_value
|
607
648
|
elif self._is_created(before=before_value, after=after_value):
|
608
649
|
dominant_value = after_value
|
@@ -611,6 +652,7 @@ class ChangeSetModel:
|
|
611
652
|
else:
|
612
653
|
dominant_value = unset
|
613
654
|
if dominant_value is not unset:
|
655
|
+
dominant_type_name = self._type_name_of(dominant_value)
|
614
656
|
if self._is_terminal(value=dominant_value):
|
615
657
|
value = self._visit_terminal_value(
|
616
658
|
scope=scope, before_value=before_value, after_value=after_value
|
@@ -623,6 +665,16 @@ class ChangeSetModel:
|
|
623
665
|
value = self._visit_array(
|
624
666
|
scope=scope, before_array=before_value, after_array=after_value
|
625
667
|
)
|
668
|
+
elif self._is_intrinsic_function_name(dominant_type_name):
|
669
|
+
intrinsic_function_scope, (before_arguments, after_arguments) = (
|
670
|
+
self._safe_access_in(scope, dominant_type_name, before_value, after_value)
|
671
|
+
)
|
672
|
+
value = self._visit_intrinsic_function(
|
673
|
+
scope=scope,
|
674
|
+
intrinsic_function=dominant_type_name,
|
675
|
+
before_arguments=before_arguments,
|
676
|
+
after_arguments=after_arguments,
|
677
|
+
)
|
626
678
|
else:
|
627
679
|
raise RuntimeError(f"Unsupported type {type(dominant_value)}")
|
628
680
|
# Case: type divergence.
|
@@ -644,24 +696,24 @@ class ChangeSetModel:
|
|
644
696
|
if isinstance(node_property, NodeProperty):
|
645
697
|
return node_property
|
646
698
|
|
699
|
+
value = self._visit_value(
|
700
|
+
scope=scope, before_value=before_property, after_value=after_property
|
701
|
+
)
|
647
702
|
if self._is_created(before=before_property, after=after_property):
|
648
703
|
node_property = NodeProperty(
|
649
704
|
scope=scope,
|
650
705
|
change_type=ChangeType.CREATED,
|
651
706
|
name=property_name,
|
652
|
-
value=
|
707
|
+
value=value,
|
653
708
|
)
|
654
709
|
elif self._is_removed(before=before_property, after=after_property):
|
655
710
|
node_property = NodeProperty(
|
656
711
|
scope=scope,
|
657
712
|
change_type=ChangeType.REMOVED,
|
658
713
|
name=property_name,
|
659
|
-
value=
|
714
|
+
value=value,
|
660
715
|
)
|
661
716
|
else:
|
662
|
-
value = self._visit_value(
|
663
|
-
scope=scope, before_value=before_property, after_value=after_property
|
664
|
-
)
|
665
717
|
node_property = NodeProperty(
|
666
718
|
scope=scope, change_type=value.change_type, name=property_name, value=value
|
667
719
|
)
|
@@ -715,14 +767,17 @@ class ChangeSetModel:
|
|
715
767
|
change_type = ChangeType.UNCHANGED
|
716
768
|
|
717
769
|
# TODO: investigate behaviour with type changes, for now this is filler code.
|
718
|
-
_, type_str = self._safe_access_in(scope, TypeKey,
|
770
|
+
_, type_str = self._safe_access_in(scope, TypeKey, after_resource)
|
719
771
|
|
772
|
+
condition_reference = None
|
720
773
|
scope_condition, (before_condition, after_condition) = self._safe_access_in(
|
721
774
|
scope, ConditionKey, before_resource, after_resource
|
722
775
|
)
|
723
|
-
|
724
|
-
|
725
|
-
|
776
|
+
# TODO: condition references should be resolved for the condition's change_type?
|
777
|
+
if before_condition or after_condition:
|
778
|
+
condition_reference = self._visit_terminal_value(
|
779
|
+
scope_condition, before_condition, after_condition
|
780
|
+
)
|
726
781
|
|
727
782
|
scope_properties, (before_properties, after_properties) = self._safe_access_in(
|
728
783
|
scope, PropertiesKey, before_resource, after_resource
|
@@ -887,18 +942,9 @@ class ChangeSetModel:
|
|
887
942
|
node_condition = self._visited_scopes.get(scope)
|
888
943
|
if isinstance(node_condition, NodeCondition):
|
889
944
|
return node_condition
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
if len(function_names) == 1:
|
894
|
-
body = self._visit_object(
|
895
|
-
scope=scope, before_object=before_condition, after_object=after_condition
|
896
|
-
)
|
897
|
-
else:
|
898
|
-
body = self._visit_divergence(
|
899
|
-
scope=scope, before_value=before_condition, after_value=after_condition
|
900
|
-
)
|
901
|
-
|
945
|
+
body = self._visit_value(
|
946
|
+
scope=scope, before_value=before_condition, after_value=after_condition
|
947
|
+
)
|
902
948
|
node_condition = NodeCondition(
|
903
949
|
scope=scope, change_type=body.change_type, name=condition_name, body=body
|
904
950
|
)
|
@@ -932,6 +978,64 @@ class ChangeSetModel:
|
|
932
978
|
self._visited_scopes[scope] = node_conditions
|
933
979
|
return node_conditions
|
934
980
|
|
981
|
+
def _visit_output(
|
982
|
+
self, scope: Scope, name: str, before_output: Maybe[dict], after_output: Maybe[dict]
|
983
|
+
) -> NodeOutput:
|
984
|
+
change_type = ChangeType.UNCHANGED
|
985
|
+
scope_value, (before_value, after_value) = self._safe_access_in(
|
986
|
+
scope, ValueKey, before_output, after_output
|
987
|
+
)
|
988
|
+
value = self._visit_value(scope_value, before_value, after_value)
|
989
|
+
change_type = change_type.for_child(value.change_type)
|
990
|
+
|
991
|
+
export: Optional[ChangeSetEntity] = None
|
992
|
+
scope_export, (before_export, after_export) = self._safe_access_in(
|
993
|
+
scope, ExportKey, before_output, after_output
|
994
|
+
)
|
995
|
+
if before_export or after_export:
|
996
|
+
export = self._visit_value(scope_export, before_export, after_export)
|
997
|
+
change_type = change_type.for_child(export.change_type)
|
998
|
+
|
999
|
+
# TODO: condition references should be resolved for the condition's change_type?
|
1000
|
+
condition_reference: Optional[TerminalValue] = None
|
1001
|
+
scope_condition, (before_condition, after_condition) = self._safe_access_in(
|
1002
|
+
scope, ConditionKey, before_output, after_output
|
1003
|
+
)
|
1004
|
+
if before_condition or after_condition:
|
1005
|
+
condition_reference = self._visit_terminal_value(
|
1006
|
+
scope_condition, before_condition, after_condition
|
1007
|
+
)
|
1008
|
+
change_type = change_type.for_child(condition_reference.change_type)
|
1009
|
+
|
1010
|
+
return NodeOutput(
|
1011
|
+
scope=scope,
|
1012
|
+
change_type=change_type,
|
1013
|
+
name=name,
|
1014
|
+
value=value,
|
1015
|
+
export=export,
|
1016
|
+
conditional_reference=condition_reference,
|
1017
|
+
)
|
1018
|
+
|
1019
|
+
def _visit_outputs(
|
1020
|
+
self, scope: Scope, before_outputs: Maybe[dict], after_outputs: Maybe[dict]
|
1021
|
+
) -> NodeOutputs:
|
1022
|
+
change_type = ChangeType.UNCHANGED
|
1023
|
+
outputs: list[NodeOutput] = list()
|
1024
|
+
output_names: list[str] = self._safe_keys_of(before_outputs, after_outputs)
|
1025
|
+
for output_name in output_names:
|
1026
|
+
scope_output, (before_output, after_output) = self._safe_access_in(
|
1027
|
+
scope, output_name, before_outputs, after_outputs
|
1028
|
+
)
|
1029
|
+
output = self._visit_output(
|
1030
|
+
scope=scope_output,
|
1031
|
+
name=output_name,
|
1032
|
+
before_output=before_output,
|
1033
|
+
after_output=after_output,
|
1034
|
+
)
|
1035
|
+
outputs.append(output)
|
1036
|
+
change_type = change_type.for_child(output.change_type)
|
1037
|
+
return NodeOutputs(scope=scope, change_type=change_type, outputs=outputs)
|
1038
|
+
|
935
1039
|
def _model(self, before_template: Maybe[dict], after_template: Maybe[dict]) -> NodeTemplate:
|
936
1040
|
root_scope = Scope()
|
937
1041
|
# TODO: visit other child types
|
@@ -970,6 +1074,13 @@ class ChangeSetModel:
|
|
970
1074
|
after_resources=after_resources,
|
971
1075
|
)
|
972
1076
|
|
1077
|
+
outputs_scope, (before_outputs, after_outputs) = self._safe_access_in(
|
1078
|
+
root_scope, OutputsKey, before_template, after_template
|
1079
|
+
)
|
1080
|
+
outputs = self._visit_outputs(
|
1081
|
+
scope=outputs_scope, before_outputs=before_outputs, after_outputs=after_outputs
|
1082
|
+
)
|
1083
|
+
|
973
1084
|
# TODO: compute the change_type of the template properly.
|
974
1085
|
return NodeTemplate(
|
975
1086
|
scope=root_scope,
|
@@ -978,6 +1089,7 @@ class ChangeSetModel:
|
|
978
1089
|
parameters=parameters,
|
979
1090
|
conditions=conditions,
|
980
1091
|
resources=resources,
|
1092
|
+
outputs=outputs,
|
981
1093
|
)
|
982
1094
|
|
983
1095
|
def _retrieve_condition_if_exists(self, condition_name: str) -> Optional[NodeCondition]:
|
@@ -1090,13 +1202,30 @@ class ChangeSetModel:
|
|
1090
1202
|
break
|
1091
1203
|
return parent_change_type
|
1092
1204
|
|
1205
|
+
@staticmethod
|
1206
|
+
def _name_if_intrinsic_function(value: Maybe[Any]) -> Optional[str]:
|
1207
|
+
if isinstance(value, dict):
|
1208
|
+
keys = ChangeSetModel._safe_keys_of(value)
|
1209
|
+
if len(keys) == 1:
|
1210
|
+
key_name = keys[0]
|
1211
|
+
if ChangeSetModel._is_intrinsic_function_name(key_name):
|
1212
|
+
return key_name
|
1213
|
+
return None
|
1214
|
+
|
1215
|
+
@staticmethod
|
1216
|
+
def _type_name_of(value: Maybe[Any]) -> str:
|
1217
|
+
maybe_intrinsic_function_name = ChangeSetModel._name_if_intrinsic_function(value)
|
1218
|
+
if maybe_intrinsic_function_name is not None:
|
1219
|
+
return maybe_intrinsic_function_name
|
1220
|
+
return type(value).__name__
|
1221
|
+
|
1093
1222
|
@staticmethod
|
1094
1223
|
def _is_terminal(value: Any) -> bool:
|
1095
1224
|
return type(value) in {int, float, bool, str, None, NothingType}
|
1096
1225
|
|
1097
1226
|
@staticmethod
|
1098
1227
|
def _is_object(value: Any) -> bool:
|
1099
|
-
return isinstance(value, dict)
|
1228
|
+
return isinstance(value, dict) and ChangeSetModel._name_if_intrinsic_function(value) is None
|
1100
1229
|
|
1101
1230
|
@staticmethod
|
1102
1231
|
def _is_array(value: Any) -> bool:
|
@@ -7,12 +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,
|
14
16
|
NodeMapping,
|
15
17
|
NodeObject,
|
18
|
+
NodeOutput,
|
19
|
+
NodeOutputs,
|
16
20
|
NodeParameter,
|
17
21
|
NodeProperties,
|
18
22
|
NodeProperty,
|
@@ -26,6 +30,7 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
26
30
|
TerminalValueModified,
|
27
31
|
TerminalValueRemoved,
|
28
32
|
TerminalValueUnchanged,
|
33
|
+
ValueKey,
|
29
34
|
)
|
30
35
|
from localstack.services.cloudformation.engine.v2.change_set_model_visitor import (
|
31
36
|
ChangeSetModelVisitor,
|
@@ -47,14 +52,20 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
47
52
|
_node_template: Final[NodeTemplate]
|
48
53
|
_changes: Final[cfn_api.Changes]
|
49
54
|
_describe_unit_cache: dict[Scope, DescribeUnit]
|
55
|
+
_include_property_values: Final[cfn_api.IncludePropertyValues | None]
|
50
56
|
|
51
|
-
def __init__(
|
57
|
+
def __init__(
|
58
|
+
self,
|
59
|
+
node_template: NodeTemplate,
|
60
|
+
include_property_values: cfn_api.IncludePropertyValues | None = None,
|
61
|
+
):
|
52
62
|
self._node_template = node_template
|
53
63
|
self._changes = list()
|
54
64
|
self._describe_unit_cache = dict()
|
55
|
-
self.
|
65
|
+
self._include_property_values = include_property_values
|
56
66
|
|
57
67
|
def get_changes(self) -> cfn_api.Changes:
|
68
|
+
self.visit(self._node_template)
|
58
69
|
return self._changes
|
59
70
|
|
60
71
|
@staticmethod
|
@@ -112,12 +123,13 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
112
123
|
return parameter_unit
|
113
124
|
|
114
125
|
# TODO: check for KNOWN AFTER APPLY values for logical ids coming from intrinsic functions as arguments.
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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)
|
121
133
|
|
122
134
|
def _resolve_mapping(self, map_name: str, top_level_key: str, second_level_key) -> DescribeUnit:
|
123
135
|
# TODO: add support for nested intrinsic functions, and KNOWN AFTER APPLY logical ids.
|
@@ -210,29 +222,37 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
210
222
|
arguments_unit = self.visit(node_intrinsic_function.arguments)
|
211
223
|
# TODO: validate the return value according to the spec.
|
212
224
|
before_argument_list = arguments_unit.before_context
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
225
|
+
after_argument_list = arguments_unit.after_context
|
226
|
+
|
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
|
221
239
|
|
222
|
-
|
223
|
-
if
|
240
|
+
after_context = None
|
241
|
+
if after_argument_list:
|
224
242
|
after_context = CHANGESET_KNOWN_AFTER_APPLY
|
225
|
-
|
226
|
-
|
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
|
227
255
|
|
228
|
-
match node_intrinsic_function.change_type:
|
229
|
-
case ChangeType.MODIFIED:
|
230
|
-
return DescribeUnit(before_context=before_context, after_context=after_context)
|
231
|
-
case ChangeType.CREATED:
|
232
|
-
return DescribeUnit(after_context=after_context)
|
233
|
-
case ChangeType.REMOVED:
|
234
|
-
return DescribeUnit(before_context=before_context)
|
235
|
-
# Unchanged
|
236
256
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
237
257
|
|
238
258
|
def visit_node_intrinsic_function_fn_equals(
|
@@ -342,12 +362,16 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
342
362
|
|
343
363
|
# TODO: add tests with created and deleted parameters and verify this logic holds.
|
344
364
|
before_logical_id = arguments_unit.before_context
|
345
|
-
|
346
|
-
|
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
|
347
369
|
|
348
370
|
after_logical_id = arguments_unit.after_context
|
349
|
-
|
350
|
-
|
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
|
351
375
|
|
352
376
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
353
377
|
|
@@ -406,21 +430,71 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
406
430
|
)
|
407
431
|
return DescribeUnit(before_context=before_context, after_context=after_context)
|
408
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
|
+
|
409
486
|
def visit_node_resource(self, node_resource: NodeResource) -> DescribeUnit:
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
if change_type == ChangeType.UNCHANGED:
|
422
|
-
# TODO
|
423
|
-
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
|
424
498
|
|
425
499
|
resource_change = cfn_api.ResourceChange()
|
426
500
|
resource_change["LogicalResourceId"] = node_resource.name
|
@@ -432,28 +506,28 @@ class ChangeSetModelDescriber(ChangeSetModelVisitor):
|
|
432
506
|
)
|
433
507
|
|
434
508
|
properties_describe_unit = self.visit(node_resource.properties)
|
435
|
-
match change_type:
|
436
|
-
case ChangeType.MODIFIED:
|
437
|
-
resource_change["Action"] = cfn_api.ChangeAction.Modify
|
438
|
-
resource_change["BeforeContext"] = properties_describe_unit.before_context
|
439
|
-
resource_change["AfterContext"] = properties_describe_unit.after_context
|
440
|
-
case ChangeType.CREATED:
|
441
|
-
resource_change["Action"] = cfn_api.ChangeAction.Add
|
442
|
-
resource_change["AfterContext"] = properties_describe_unit.after_context
|
443
|
-
case ChangeType.REMOVED:
|
444
|
-
resource_change["Action"] = cfn_api.ChangeAction.Remove
|
445
|
-
resource_change["BeforeContext"] = properties_describe_unit.before_context
|
446
|
-
|
447
|
-
self._changes.append(
|
448
|
-
cfn_api.Change(Type=cfn_api.ChangeType.Resource, ResourceChange=resource_change)
|
449
|
-
)
|
450
509
|
|
451
|
-
|
452
|
-
|
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
|
+
)
|
453
525
|
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
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)
|