localstack-core 4.5.1.dev61__py3-none-any.whl → 4.5.1.dev63__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/v2/change_set_model.py +22 -6
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +24 -4
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +2 -1
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +223 -210
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +33 -18
- localstack/services/cloudformation/v2/entities.py +7 -3
- localstack/services/cloudformation/v2/provider.py +104 -6
- localstack/version.py +2 -2
- {localstack_core-4.5.1.dev61.dist-info → localstack_core-4.5.1.dev63.dist-info}/METADATA +1 -1
- {localstack_core-4.5.1.dev61.dist-info → localstack_core-4.5.1.dev63.dist-info}/RECORD +18 -18
- localstack_core-4.5.1.dev63.dist-info/plux.json +1 -0
- localstack_core-4.5.1.dev61.dist-info/plux.json +0 -1
- {localstack_core-4.5.1.dev61.data → localstack_core-4.5.1.dev63.data}/scripts/localstack +0 -0
- {localstack_core-4.5.1.dev61.data → localstack_core-4.5.1.dev63.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.5.1.dev61.data → localstack_core-4.5.1.dev63.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.5.1.dev61.dist-info → localstack_core-4.5.1.dev63.dist-info}/WHEEL +0 -0
- {localstack_core-4.5.1.dev61.dist-info → localstack_core-4.5.1.dev63.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.5.1.dev61.dist-info → localstack_core-4.5.1.dev63.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.5.1.dev61.dist-info → localstack_core-4.5.1.dev63.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,9 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import base64
|
4
|
+
import copy
|
4
5
|
import re
|
5
|
-
from typing import Any, Final, Generic, Optional, TypeVar
|
6
|
+
from typing import Any, Callable, Final, Generic, Optional, TypeVar
|
6
7
|
|
7
8
|
from botocore.exceptions import ClientError
|
8
9
|
|
@@ -33,6 +34,7 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
33
34
|
NodeResource,
|
34
35
|
NodeTemplate,
|
35
36
|
Nothing,
|
37
|
+
NothingType,
|
36
38
|
Scope,
|
37
39
|
TerminalValue,
|
38
40
|
TerminalValueCreated,
|
@@ -165,19 +167,44 @@ class PreprocOutput:
|
|
165
167
|
|
166
168
|
class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
167
169
|
_change_set: Final[ChangeSet]
|
168
|
-
_node_template: Final[NodeTemplate]
|
169
170
|
_before_resolved_resources: Final[dict]
|
170
|
-
|
171
|
+
_before_cache: Final[dict[Scope, Any]]
|
172
|
+
_after_cache: Final[dict[Scope, Any]]
|
171
173
|
|
172
174
|
def __init__(self, change_set: ChangeSet):
|
173
175
|
self._change_set = change_set
|
174
|
-
self._node_template = change_set.update_model
|
175
176
|
self._before_resolved_resources = change_set.stack.resolved_resources
|
176
|
-
self.
|
177
|
+
self._before_cache = dict()
|
178
|
+
self._after_cache = dict()
|
179
|
+
|
180
|
+
def _setup_runtime_cache(self) -> None:
|
181
|
+
runtime_cache_key = self.__class__.__name__
|
182
|
+
|
183
|
+
self._before_cache.clear()
|
184
|
+
self._after_cache.clear()
|
185
|
+
|
186
|
+
before_runtime_cache = self._change_set.update_model.before_runtime_cache
|
187
|
+
if cache := before_runtime_cache.get(runtime_cache_key):
|
188
|
+
self._before_cache.update(cache)
|
189
|
+
|
190
|
+
after_runtime_cache = self._change_set.update_model.after_runtime_cache
|
191
|
+
if cache := after_runtime_cache.get(runtime_cache_key):
|
192
|
+
self._after_cache.update(cache)
|
193
|
+
|
194
|
+
def _save_runtime_cache(self) -> None:
|
195
|
+
runtime_cache_key = self.__class__.__name__
|
196
|
+
|
197
|
+
before_runtime_cache = self._change_set.update_model.before_runtime_cache
|
198
|
+
before_runtime_cache[runtime_cache_key] = copy.deepcopy(self._before_cache)
|
199
|
+
|
200
|
+
after_runtime_cache = self._change_set.update_model.after_runtime_cache
|
201
|
+
after_runtime_cache[runtime_cache_key] = copy.deepcopy(self._after_cache)
|
177
202
|
|
178
203
|
def process(self) -> None:
|
179
|
-
self.
|
180
|
-
self.
|
204
|
+
self._setup_runtime_cache()
|
205
|
+
node_template = self._change_set.update_model.node_template
|
206
|
+
self.visit(node_template)
|
207
|
+
self._save_runtime_cache()
|
181
208
|
|
182
209
|
def _get_node_resource_for(
|
183
210
|
self, resource_name: str, node_template: NodeTemplate
|
@@ -209,7 +236,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
209
236
|
# be accessible through delta objects, to ensure computation is always complete at
|
210
237
|
# every level.
|
211
238
|
_ = self._get_node_resource_for(
|
212
|
-
resource_name=resource_logical_id,
|
239
|
+
resource_name=resource_logical_id,
|
240
|
+
node_template=self._change_set.update_model.node_template,
|
213
241
|
)
|
214
242
|
resolved_resource = resolved_resources.get(resource_logical_id)
|
215
243
|
if resolved_resource is None:
|
@@ -241,7 +269,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
241
269
|
)
|
242
270
|
|
243
271
|
def _get_node_mapping(self, map_name: str) -> NodeMapping:
|
244
|
-
mappings: list[NodeMapping] = self.
|
272
|
+
mappings: list[NodeMapping] = self._change_set.update_model.node_template.mappings.mappings
|
245
273
|
# TODO: another scenarios suggesting property lookups might be preferable.
|
246
274
|
for mapping in mappings:
|
247
275
|
if mapping.name == map_name:
|
@@ -250,7 +278,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
250
278
|
raise RuntimeError(f"Undefined '{map_name}' mapping")
|
251
279
|
|
252
280
|
def _get_node_parameter_if_exists(self, parameter_name: str) -> Maybe[NodeParameter]:
|
253
|
-
parameters: list[NodeParameter] =
|
281
|
+
parameters: list[NodeParameter] = (
|
282
|
+
self._change_set.update_model.node_template.parameters.parameters
|
283
|
+
)
|
254
284
|
# TODO: another scenarios suggesting property lookups might be preferable.
|
255
285
|
for parameter in parameters:
|
256
286
|
if parameter.name == parameter_name:
|
@@ -259,7 +289,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
259
289
|
return Nothing
|
260
290
|
|
261
291
|
def _get_node_condition_if_exists(self, condition_name: str) -> Maybe[NodeCondition]:
|
262
|
-
conditions: list[NodeCondition] =
|
292
|
+
conditions: list[NodeCondition] = (
|
293
|
+
self._change_set.update_model.node_template.conditions.conditions
|
294
|
+
)
|
263
295
|
# TODO: another scenarios suggesting property lookups might be preferable.
|
264
296
|
for condition in conditions:
|
265
297
|
if condition.name == condition_name:
|
@@ -307,7 +339,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
307
339
|
return parameter_delta
|
308
340
|
|
309
341
|
node_resource = self._get_node_resource_for(
|
310
|
-
resource_name=logical_id, node_template=self.
|
342
|
+
resource_name=logical_id, node_template=self._change_set.update_model.node_template
|
311
343
|
)
|
312
344
|
resource_delta = self.visit(node_resource)
|
313
345
|
before = resource_delta.before
|
@@ -327,14 +359,65 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
327
359
|
return mapping_value_delta
|
328
360
|
|
329
361
|
def visit(self, change_set_entity: ChangeSetEntity) -> PreprocEntityDelta:
|
330
|
-
|
331
|
-
if
|
332
|
-
|
333
|
-
|
362
|
+
entity_scope = change_set_entity.scope
|
363
|
+
if entity_scope in self._before_cache and entity_scope in self._after_cache:
|
364
|
+
before = self._before_cache[entity_scope]
|
365
|
+
after = self._after_cache[entity_scope]
|
366
|
+
return PreprocEntityDelta(before=before, after=after)
|
334
367
|
delta = super().visit(change_set_entity=change_set_entity)
|
335
|
-
|
368
|
+
if isinstance(delta, PreprocEntityDelta):
|
369
|
+
self._before_cache[entity_scope] = delta.before
|
370
|
+
self._after_cache[entity_scope] = delta.after
|
336
371
|
return delta
|
337
372
|
|
373
|
+
def _cached_apply(
|
374
|
+
self, scope: Scope, arguments_delta: PreprocEntityDelta, resolver: Callable[[Any], Any]
|
375
|
+
) -> PreprocEntityDelta:
|
376
|
+
"""
|
377
|
+
Applies the resolver function to the given input delta if and only if the required
|
378
|
+
values are not already present in the runtime caches. This function handles both
|
379
|
+
the 'before' and 'after' components of the delta independently.
|
380
|
+
|
381
|
+
The resolver function receives either the 'before' or 'after' value from the input
|
382
|
+
delta and returns a resolved value. If the result returned by the resolver is
|
383
|
+
itself a PreprocEntityDelta, the function automatically extracts the appropriate
|
384
|
+
component from it: the 'before' value if the input was 'before', and the 'after'
|
385
|
+
value if the input was 'after'.
|
386
|
+
|
387
|
+
This function only reads from the cache and does not update it. It is the caller's
|
388
|
+
responsibility to handle caching, either manually or via the upstream visit method
|
389
|
+
of this class.
|
390
|
+
|
391
|
+
Args:
|
392
|
+
scope (Scope): The current scope used as a key for cache lookup.
|
393
|
+
arguments_delta (PreprocEntityDelta): The delta containing 'before' and 'after' values to resolve.
|
394
|
+
resolver (Callable[[Any], Any]): Function to apply on uncached 'before' or 'after' argument values.
|
395
|
+
|
396
|
+
Returns:
|
397
|
+
PreprocEntityDelta: A new delta with resolved 'before' and 'after' values.
|
398
|
+
"""
|
399
|
+
|
400
|
+
# TODO: Update all visit_* methods in this class and its subclasses to use this function.
|
401
|
+
# This ensures maximal reuse of precomputed 'before' (and 'after') values from
|
402
|
+
# prior runtimes on the change sets template, thus avoiding unnecessary recomputation.
|
403
|
+
|
404
|
+
arguments_before = arguments_delta.before
|
405
|
+
arguments_after = arguments_delta.after
|
406
|
+
|
407
|
+
before = self._before_cache.get(scope, Nothing)
|
408
|
+
if is_nothing(before) and not is_nothing(arguments_before):
|
409
|
+
before = resolver(arguments_before)
|
410
|
+
if isinstance(before, PreprocEntityDelta):
|
411
|
+
before = before.before
|
412
|
+
|
413
|
+
after = self._after_cache.get(scope, Nothing)
|
414
|
+
if is_nothing(after) and not is_nothing(arguments_after):
|
415
|
+
after = resolver(arguments_after)
|
416
|
+
if isinstance(after, PreprocEntityDelta):
|
417
|
+
after = after.after
|
418
|
+
|
419
|
+
return PreprocEntityDelta(before=before, after=after)
|
420
|
+
|
338
421
|
def visit_terminal_value_modified(
|
339
422
|
self, terminal_value_modified: TerminalValueModified
|
340
423
|
) -> PreprocEntityDelta:
|
@@ -391,7 +474,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
391
474
|
attribute_name = arguments_list[1]
|
392
475
|
|
393
476
|
node_resource = self._get_node_resource_for(
|
394
|
-
resource_name=logical_name_of_resource,
|
477
|
+
resource_name=logical_name_of_resource,
|
478
|
+
node_template=self._change_set.update_model.node_template,
|
395
479
|
)
|
396
480
|
node_property: Optional[NodeProperty] = self._get_node_property_for(
|
397
481
|
property_name=attribute_name, node_resource=node_resource
|
@@ -423,12 +507,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
423
507
|
before_arguments: Maybe[str | list[str]] = arguments_delta.before
|
424
508
|
after_arguments: Maybe[str | list[str]] = arguments_delta.after
|
425
509
|
|
426
|
-
before = Nothing
|
427
|
-
if not is_nothing(before_arguments):
|
510
|
+
before = self._before_cache.get(node_intrinsic_function.scope, Nothing)
|
511
|
+
if is_nothing(before) and not is_nothing(before_arguments):
|
428
512
|
before = self._resolve_attribute(arguments=before_arguments, select_before=True)
|
429
513
|
|
430
|
-
after = Nothing
|
431
|
-
if not is_nothing(after_arguments):
|
514
|
+
after = self._after_cache.get(node_intrinsic_function.scope, Nothing)
|
515
|
+
if is_nothing(after) and not is_nothing(after_arguments):
|
432
516
|
after = self._resolve_attribute(arguments=after_arguments, select_before=False)
|
433
517
|
|
434
518
|
return PreprocEntityDelta(before=before, after=after)
|
@@ -436,24 +520,21 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
436
520
|
def visit_node_intrinsic_function_fn_equals(
|
437
521
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
438
522
|
) -> PreprocEntityDelta:
|
523
|
+
# TODO: add argument shape validation.
|
524
|
+
def _compute_fn_equals(args: list[Any]) -> bool:
|
525
|
+
return args[0] == args[1]
|
526
|
+
|
439
527
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
if after_values:
|
447
|
-
after = after_values[0] == after_values[1]
|
448
|
-
return PreprocEntityDelta(before=before, after=after)
|
528
|
+
delta = self._cached_apply(
|
529
|
+
scope=node_intrinsic_function.scope,
|
530
|
+
arguments_delta=arguments_delta,
|
531
|
+
resolver=_compute_fn_equals,
|
532
|
+
)
|
533
|
+
return delta
|
449
534
|
|
450
535
|
def visit_node_intrinsic_function_fn_if(
|
451
536
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
452
537
|
) -> PreprocEntityDelta:
|
453
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
454
|
-
arguments_before = arguments_delta.before
|
455
|
-
arguments_after = arguments_delta.after
|
456
|
-
|
457
538
|
def _compute_delta_for_if_statement(args: list[Any]) -> PreprocEntityDelta:
|
458
539
|
condition_name = args[0]
|
459
540
|
boolean_expression_delta = self._resolve_condition(logical_id=condition_name)
|
@@ -462,74 +543,57 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
462
543
|
after=args[1] if boolean_expression_delta.after else args[2],
|
463
544
|
)
|
464
545
|
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
after_outcome_delta = _compute_delta_for_if_statement(arguments_after)
|
473
|
-
after = after_outcome_delta.after
|
474
|
-
return PreprocEntityDelta(before=before, after=after)
|
546
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
547
|
+
delta = self._cached_apply(
|
548
|
+
scope=node_intrinsic_function.scope,
|
549
|
+
arguments_delta=arguments_delta,
|
550
|
+
resolver=_compute_delta_for_if_statement,
|
551
|
+
)
|
552
|
+
return delta
|
475
553
|
|
476
554
|
def visit_node_intrinsic_function_fn_and(
|
477
555
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
478
556
|
) -> PreprocEntityDelta:
|
479
|
-
|
480
|
-
arguments_before = arguments_delta.before
|
481
|
-
arguments_after = arguments_delta.after
|
482
|
-
|
483
|
-
def _compute_fn_and(args: list[bool]):
|
557
|
+
def _compute_fn_and(args: list[bool]) -> bool:
|
484
558
|
result = all(args)
|
485
559
|
return result
|
486
560
|
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
return PreprocEntityDelta(before=before, after=after)
|
561
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
562
|
+
delta = self._cached_apply(
|
563
|
+
scope=node_intrinsic_function.scope,
|
564
|
+
arguments_delta=arguments_delta,
|
565
|
+
resolver=_compute_fn_and,
|
566
|
+
)
|
567
|
+
return delta
|
496
568
|
|
497
569
|
def visit_node_intrinsic_function_fn_or(
|
498
570
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
499
571
|
) -> PreprocEntityDelta:
|
500
|
-
|
501
|
-
arguments_before = arguments_delta.before
|
502
|
-
arguments_after = arguments_delta.after
|
503
|
-
|
504
|
-
def _compute_fn_and(args: list[bool]):
|
572
|
+
def _compute_fn_or(args: list[bool]):
|
505
573
|
result = any(args)
|
506
574
|
return result
|
507
575
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
return PreprocEntityDelta(before=before, after=after)
|
576
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
577
|
+
delta = self._cached_apply(
|
578
|
+
scope=node_intrinsic_function.scope,
|
579
|
+
arguments_delta=arguments_delta,
|
580
|
+
resolver=_compute_fn_or,
|
581
|
+
)
|
582
|
+
return delta
|
516
583
|
|
517
584
|
def visit_node_intrinsic_function_fn_not(
|
518
585
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
519
586
|
) -> PreprocEntityDelta:
|
587
|
+
def _compute_fn_not(arg: bool) -> bool:
|
588
|
+
return not arg
|
589
|
+
|
520
590
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
after = Nothing
|
528
|
-
if not is_nothing(after_condition):
|
529
|
-
after_condition_outcome = after_condition[0]
|
530
|
-
after = not after_condition_outcome
|
531
|
-
# Implicit change type computation.
|
532
|
-
return PreprocEntityDelta(before=before, after=after)
|
591
|
+
delta = self._cached_apply(
|
592
|
+
scope=node_intrinsic_function.scope,
|
593
|
+
arguments_delta=arguments_delta,
|
594
|
+
resolver=_compute_fn_not,
|
595
|
+
)
|
596
|
+
return delta
|
533
597
|
|
534
598
|
def _compute_fn_transform(self, args: dict[str, Any]) -> Any:
|
535
599
|
# TODO: add typing to arguments before this level.
|
@@ -583,33 +647,16 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
583
647
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
584
648
|
) -> PreprocEntityDelta:
|
585
649
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
# though for this transformation before==after). Another options may be to
|
593
|
-
# have specialised caching for transformations.
|
594
|
-
|
595
|
-
# TODO: add tests to review the behaviour of CFN with changes to transformation
|
596
|
-
# function code and no changes to the template.
|
597
|
-
|
598
|
-
before = Nothing
|
599
|
-
if not is_nothing(arguments_before):
|
600
|
-
before = self._compute_fn_transform(args=arguments_before)
|
601
|
-
after = Nothing
|
602
|
-
if not is_nothing(arguments_after):
|
603
|
-
after = self._compute_fn_transform(args=arguments_after)
|
604
|
-
return PreprocEntityDelta(before=before, after=after)
|
650
|
+
delta = self._cached_apply(
|
651
|
+
scope=node_intrinsic_function.scope,
|
652
|
+
arguments_delta=arguments_delta,
|
653
|
+
resolver=self._compute_fn_transform,
|
654
|
+
)
|
655
|
+
return delta
|
605
656
|
|
606
657
|
def visit_node_intrinsic_function_fn_sub(
|
607
658
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
608
659
|
) -> PreprocEntityDelta:
|
609
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
610
|
-
arguments_before = arguments_delta.before
|
611
|
-
arguments_after = arguments_delta.after
|
612
|
-
|
613
660
|
def _compute_sub(args: str | list[Any], select_before: bool) -> str:
|
614
661
|
# TODO: add further schema validation.
|
615
662
|
string_template: str
|
@@ -695,24 +742,25 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
695
742
|
result = sub_string
|
696
743
|
return result
|
697
744
|
|
698
|
-
|
699
|
-
|
745
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
746
|
+
arguments_before = arguments_delta.before
|
747
|
+
arguments_after = arguments_delta.after
|
748
|
+
before = self._before_cache.get(node_intrinsic_function.scope, Nothing)
|
749
|
+
if is_nothing(before) and not is_nothing(arguments_before):
|
700
750
|
before = _compute_sub(args=arguments_before, select_before=True)
|
701
|
-
after = Nothing
|
702
|
-
if not is_nothing(arguments_after):
|
751
|
+
after = self._after_cache.get(node_intrinsic_function.scope, Nothing)
|
752
|
+
if is_nothing(after) and not is_nothing(arguments_after):
|
703
753
|
after = _compute_sub(args=arguments_after, select_before=False)
|
704
754
|
return PreprocEntityDelta(before=before, after=after)
|
705
755
|
|
706
756
|
def visit_node_intrinsic_function_fn_join(
|
707
757
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
708
758
|
) -> PreprocEntityDelta:
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
# TODO: add support for schema validation.
|
715
|
-
# TODO: add tests for joining non string values.
|
759
|
+
# TODO: add support for schema validation.
|
760
|
+
# TODO: add tests for joining non string values.
|
761
|
+
def _compute_fn_join(args: list[Any]) -> str | NothingType:
|
762
|
+
if not (isinstance(args, list) and len(args) == 2):
|
763
|
+
return Nothing
|
716
764
|
delimiter: str = str(args[0])
|
717
765
|
values: list[Any] = args[1]
|
718
766
|
if not isinstance(values, list):
|
@@ -731,22 +779,18 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
731
779
|
join_result = delimiter.join(str_values)
|
732
780
|
return join_result
|
733
781
|
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
return
|
782
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
783
|
+
delta = self._cached_apply(
|
784
|
+
scope=node_intrinsic_function.scope,
|
785
|
+
arguments_delta=arguments_delta,
|
786
|
+
resolver=_compute_fn_join,
|
787
|
+
)
|
788
|
+
return delta
|
741
789
|
|
742
790
|
def visit_node_intrinsic_function_fn_select(
|
743
791
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
744
792
|
):
|
745
793
|
# TODO: add further support for schema validation
|
746
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
747
|
-
arguments_before = arguments_delta.before
|
748
|
-
arguments_after = arguments_delta.after
|
749
|
-
|
750
794
|
def _compute_fn_select(args: list[Any]) -> Any:
|
751
795
|
values: list[Any] = args[1]
|
752
796
|
if not isinstance(values, list) or not values:
|
@@ -758,24 +802,18 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
758
802
|
selection = values[index]
|
759
803
|
return selection
|
760
804
|
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
return PreprocEntityDelta(before=before, after=after)
|
805
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
806
|
+
delta = self._cached_apply(
|
807
|
+
scope=node_intrinsic_function.scope,
|
808
|
+
arguments_delta=arguments_delta,
|
809
|
+
resolver=_compute_fn_select,
|
810
|
+
)
|
811
|
+
return delta
|
770
812
|
|
771
813
|
def visit_node_intrinsic_function_fn_split(
|
772
814
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
773
815
|
):
|
774
816
|
# TODO: add further support for schema validation
|
775
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
776
|
-
arguments_before = arguments_delta.before
|
777
|
-
arguments_after = arguments_delta.after
|
778
|
-
|
779
817
|
def _compute_fn_split(args: list[Any]) -> Any:
|
780
818
|
delimiter = args[0]
|
781
819
|
if not isinstance(delimiter, str) or not delimiter:
|
@@ -786,23 +824,18 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
786
824
|
split_string = source_string.split(delimiter)
|
787
825
|
return split_string
|
788
826
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
return PreprocEntityDelta(before=before, after=after)
|
827
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
828
|
+
delta = self._cached_apply(
|
829
|
+
scope=node_intrinsic_function.scope,
|
830
|
+
arguments_delta=arguments_delta,
|
831
|
+
resolver=_compute_fn_split,
|
832
|
+
)
|
833
|
+
return delta
|
798
834
|
|
799
835
|
def visit_node_intrinsic_function_fn_get_a_zs(
|
800
836
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
801
837
|
) -> PreprocEntityDelta:
|
802
838
|
# TODO: add further support for schema validation
|
803
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
804
|
-
arguments_before = arguments_delta.before
|
805
|
-
arguments_after = arguments_delta.after
|
806
839
|
|
807
840
|
def _compute_fn_get_a_zs(region) -> Any:
|
808
841
|
if not isinstance(region, str):
|
@@ -827,24 +860,18 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
827
860
|
azs = [az["ZoneName"] for az in availability_zones]
|
828
861
|
return azs
|
829
862
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
return PreprocEntityDelta(before=before, after=after)
|
863
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
864
|
+
delta = self._cached_apply(
|
865
|
+
scope=node_intrinsic_function.scope,
|
866
|
+
arguments_delta=arguments_delta,
|
867
|
+
resolver=_compute_fn_get_a_zs,
|
868
|
+
)
|
869
|
+
return delta
|
839
870
|
|
840
871
|
def visit_node_intrinsic_function_fn_base64(
|
841
872
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
842
873
|
) -> PreprocEntityDelta:
|
843
874
|
# TODO: add further support for schema validation
|
844
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
845
|
-
arguments_before = arguments_delta.before
|
846
|
-
arguments_after = arguments_delta.after
|
847
|
-
|
848
875
|
def _compute_fn_base_64(string) -> Any:
|
849
876
|
if not isinstance(string, str):
|
850
877
|
raise RuntimeError(f"Invalid valueToEncode for Fn::Base64: '{string}'")
|
@@ -852,15 +879,13 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
852
879
|
base64_string = to_str(base64.b64encode(to_bytes(string)))
|
853
880
|
return base64_string
|
854
881
|
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
return PreprocEntityDelta(before=before, after=after)
|
882
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
883
|
+
delta = self._cached_apply(
|
884
|
+
scope=node_intrinsic_function.scope,
|
885
|
+
arguments_delta=arguments_delta,
|
886
|
+
resolver=_compute_fn_base_64,
|
887
|
+
)
|
888
|
+
return delta
|
864
889
|
|
865
890
|
def visit_node_intrinsic_function_fn_find_in_map(
|
866
891
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
@@ -941,52 +966,40 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
941
966
|
def visit_node_intrinsic_function_ref(
|
942
967
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
943
968
|
) -> PreprocEntityDelta:
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
before_delta = self._resolve_reference(logical_id=before_logical_id)
|
952
|
-
before = before_delta.before
|
953
|
-
if isinstance(before, PreprocResource):
|
954
|
-
before = before.physical_resource_id
|
955
|
-
|
956
|
-
after = Nothing
|
957
|
-
if not is_nothing(after_logical_id):
|
958
|
-
after_delta = self._resolve_reference(logical_id=after_logical_id)
|
959
|
-
after = after_delta.after
|
960
|
-
if isinstance(after, PreprocResource):
|
961
|
-
after = after.physical_resource_id
|
969
|
+
def _compute_fn_ref(logical_id: str) -> PreprocEntityDelta:
|
970
|
+
reference_delta: PreprocEntityDelta = self._resolve_reference(logical_id=logical_id)
|
971
|
+
if isinstance(before := reference_delta.before, PreprocResource):
|
972
|
+
reference_delta.before = before.physical_resource_id
|
973
|
+
if isinstance(after := reference_delta.after, PreprocResource):
|
974
|
+
reference_delta.after = after.physical_resource_id
|
975
|
+
return reference_delta
|
962
976
|
|
963
|
-
|
977
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
978
|
+
delta = self._cached_apply(
|
979
|
+
scope=node_intrinsic_function.scope,
|
980
|
+
arguments_delta=arguments_delta,
|
981
|
+
resolver=_compute_fn_ref,
|
982
|
+
)
|
983
|
+
return delta
|
964
984
|
|
965
985
|
def visit_node_intrinsic_function_condition(
|
966
986
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
967
987
|
) -> PreprocEntityDelta:
|
968
988
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
969
|
-
before_condition_name = arguments_delta.before
|
970
|
-
after_condition_name = arguments_delta.after
|
971
989
|
|
972
990
|
def _delta_of_condition(name: str) -> PreprocEntityDelta:
|
973
991
|
node_condition = self._get_node_condition_if_exists(condition_name=name)
|
974
992
|
if is_nothing(node_condition):
|
975
993
|
raise RuntimeError(f"Undefined condition '{name}'")
|
976
|
-
|
977
|
-
return
|
978
|
-
|
979
|
-
before = Nothing
|
980
|
-
if not is_nothing(before_condition_name):
|
981
|
-
before_delta = _delta_of_condition(before_condition_name)
|
982
|
-
before = before_delta.before
|
983
|
-
|
984
|
-
after = Nothing
|
985
|
-
if not is_nothing(after_condition_name):
|
986
|
-
after_delta = _delta_of_condition(after_condition_name)
|
987
|
-
after = after_delta.after
|
994
|
+
condition_delta = self.visit(node_condition)
|
995
|
+
return condition_delta
|
988
996
|
|
989
|
-
|
997
|
+
delta = self._cached_apply(
|
998
|
+
resolver=_delta_of_condition,
|
999
|
+
scope=node_intrinsic_function.scope,
|
1000
|
+
arguments_delta=arguments_delta,
|
1001
|
+
)
|
1002
|
+
return delta
|
990
1003
|
|
991
1004
|
def visit_node_array(self, node_array: NodeArray) -> PreprocEntityDelta:
|
992
1005
|
node_change_type = node_array.change_type
|