localstack-core 4.7.1.dev49__py3-none-any.whl → 4.10.1.dev12__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/cloudformation/__init__.py +18 -4
- localstack/aws/api/cloudwatch/__init__.py +41 -1
- localstack/aws/api/config/__init__.py +4 -0
- localstack/aws/api/core.py +6 -2
- localstack/aws/api/dynamodb/__init__.py +30 -0
- localstack/aws/api/ec2/__init__.py +1522 -65
- localstack/aws/api/iam/__init__.py +7 -0
- localstack/aws/api/kinesis/__init__.py +19 -0
- localstack/aws/api/kms/__init__.py +6 -0
- localstack/aws/api/lambda_/__init__.py +13 -0
- localstack/aws/api/logs/__init__.py +15 -0
- localstack/aws/api/redshift/__init__.py +9 -3
- localstack/aws/api/route53/__init__.py +5 -0
- localstack/aws/api/s3/__init__.py +12 -0
- localstack/aws/api/s3control/__init__.py +54 -0
- localstack/aws/api/ssm/__init__.py +2 -0
- localstack/aws/api/transcribe/__init__.py +17 -0
- localstack/aws/client.py +7 -2
- localstack/aws/forwarder.py +52 -5
- localstack/aws/handlers/analytics.py +1 -1
- localstack/aws/handlers/internal_requests.py +6 -1
- localstack/aws/handlers/logging.py +12 -2
- localstack/aws/handlers/metric_handler.py +41 -1
- localstack/aws/handlers/service.py +40 -20
- localstack/aws/mocking.py +2 -2
- localstack/aws/patches.py +2 -2
- localstack/aws/protocol/parser.py +459 -32
- localstack/aws/protocol/serializer.py +689 -69
- localstack/aws/protocol/service_router.py +120 -20
- localstack/aws/protocol/validate.py +1 -1
- localstack/aws/scaffold.py +1 -1
- localstack/aws/skeleton.py +4 -2
- localstack/aws/spec-patches.json +58 -0
- localstack/aws/spec.py +37 -16
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +6 -6
- localstack/cli/lpm.py +3 -4
- localstack/cli/plugins.py +1 -1
- localstack/cli/profiles.py +1 -2
- localstack/config.py +25 -18
- localstack/constants.py +4 -29
- localstack/dev/kubernetes/__main__.py +130 -7
- localstack/dev/run/configurators.py +1 -4
- localstack/dev/run/paths.py +1 -1
- localstack/dns/plugins.py +5 -1
- localstack/dns/server.py +13 -4
- localstack/logging/format.py +3 -3
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/packages/plugins.py +0 -8
- localstack/runtime/analytics.py +3 -0
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +2 -2
- localstack/runtime/main.py +5 -5
- localstack/runtime/patches.py +2 -2
- localstack/services/apigateway/helpers.py +1 -4
- localstack/services/apigateway/legacy/helpers.py +7 -8
- localstack/services/apigateway/legacy/integration.py +4 -3
- localstack/services/apigateway/legacy/invocations.py +6 -5
- localstack/services/apigateway/legacy/provider.py +148 -68
- localstack/services/apigateway/legacy/templates.py +1 -1
- localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
- localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
- localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
- localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
- localstack/services/apigateway/next_gen/provider.py +5 -0
- localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
- localstack/services/cloudformation/api_utils.py +4 -8
- localstack/services/cloudformation/cfn_utils.py +1 -1
- localstack/services/cloudformation/engine/entities.py +14 -4
- localstack/services/cloudformation/engine/template_deployer.py +6 -4
- localstack/services/cloudformation/engine/transformers.py +6 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
- localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
- localstack/services/cloudformation/engine/v2/resolving.py +7 -5
- localstack/services/cloudformation/engine/yaml_parser.py +9 -2
- localstack/services/cloudformation/provider.py +7 -5
- localstack/services/cloudformation/resource_provider.py +7 -1
- localstack/services/cloudformation/resources.py +24149 -0
- localstack/services/cloudformation/service_models.py +2 -2
- localstack/services/cloudformation/v2/entities.py +19 -9
- localstack/services/cloudformation/v2/provider.py +336 -106
- localstack/services/cloudformation/v2/types.py +13 -7
- localstack/services/cloudformation/v2/utils.py +4 -1
- localstack/services/cloudwatch/alarm_scheduler.py +4 -1
- localstack/services/cloudwatch/provider.py +18 -13
- localstack/services/cloudwatch/provider_v2.py +25 -28
- localstack/services/dynamodb/packages.py +2 -1
- localstack/services/dynamodb/provider.py +42 -0
- localstack/services/dynamodb/server.py +2 -2
- localstack/services/dynamodb/v2/provider.py +42 -0
- localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
- localstack/services/edge.py +1 -1
- localstack/services/es/provider.py +2 -2
- localstack/services/events/event_rule_engine.py +31 -13
- localstack/services/events/models.py +4 -5
- localstack/services/events/provider.py +17 -14
- localstack/services/events/target.py +17 -9
- localstack/services/events/v1/provider.py +5 -5
- localstack/services/firehose/provider.py +14 -4
- localstack/services/iam/provider.py +11 -116
- localstack/services/iam/resources/policy_simulator.py +133 -0
- localstack/services/kinesis/models.py +15 -2
- localstack/services/kinesis/provider.py +86 -3
- localstack/services/kms/provider.py +14 -5
- localstack/services/lambda_/api_utils.py +6 -3
- localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
- localstack/services/lambda_/invocation/event_manager.py +1 -1
- localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
- localstack/services/lambda_/invocation/lambda_models.py +10 -7
- localstack/services/lambda_/invocation/lambda_service.py +5 -1
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +4 -3
- localstack/services/lambda_/provider_utils.py +1 -1
- localstack/services/logs/provider.py +36 -19
- localstack/services/moto.py +2 -1
- localstack/services/opensearch/cluster.py +15 -7
- localstack/services/opensearch/packages.py +26 -7
- localstack/services/opensearch/provider.py +8 -2
- localstack/services/opensearch/versions.py +56 -7
- localstack/services/plugins.py +11 -7
- localstack/services/providers.py +10 -2
- localstack/services/redshift/provider.py +0 -21
- localstack/services/s3/constants.py +5 -2
- localstack/services/s3/cors.py +4 -4
- localstack/services/s3/models.py +1 -1
- localstack/services/s3/notifications.py +55 -39
- localstack/services/s3/presigned_url.py +35 -54
- localstack/services/s3/provider.py +73 -15
- localstack/services/s3/utils.py +42 -22
- localstack/services/s3/validation.py +46 -32
- localstack/services/s3/website_hosting.py +4 -2
- localstack/services/ses/provider.py +18 -8
- localstack/services/sns/constants.py +7 -1
- localstack/services/sns/executor.py +9 -2
- localstack/services/sns/provider.py +8 -5
- localstack/services/sns/publisher.py +31 -16
- localstack/services/sns/v2/models.py +167 -0
- localstack/services/sns/v2/provider.py +867 -0
- localstack/services/sns/v2/utils.py +130 -0
- localstack/services/sqs/constants.py +1 -1
- localstack/services/sqs/developer_api.py +205 -0
- localstack/services/sqs/models.py +48 -5
- localstack/services/sqs/provider.py +38 -311
- localstack/services/sqs/query_api.py +6 -2
- localstack/services/sqs/utils.py +121 -2
- localstack/services/ssm/provider.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
- localstack/services/stepfunctions/asl/eval/environment.py +1 -1
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
- localstack/services/stepfunctions/backend/execution.py +2 -1
- localstack/services/stores.py +1 -1
- localstack/services/transcribe/provider.py +6 -1
- localstack/state/codecs.py +61 -0
- localstack/state/core.py +11 -5
- localstack/state/pickle.py +10 -49
- localstack/testing/aws/cloudformation_utils.py +1 -1
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/cloudformation/transformers.py +0 -0
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +33 -31
- localstack/testing/pytest/in_memory_localstack.py +0 -4
- localstack/testing/pytest/marking.py +38 -11
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/testing/snapshots/transformer_utility.py +6 -1
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +6 -4
- localstack/utils/analytics/metrics/counter.py +8 -15
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_providers.py +19 -0
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/asyncio.py +2 -2
- localstack/utils/aws/arns.py +24 -29
- localstack/utils/aws/aws_responses.py +8 -8
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/dead_letter_queue.py +1 -5
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +1 -1
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +21 -13
- localstack/utils/catalog/catalog.py +139 -0
- localstack/utils/catalog/catalog_loader.py +119 -0
- localstack/utils/catalog/common.py +58 -0
- localstack/utils/catalog/plugins.py +28 -0
- localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
- localstack/utils/collections.py +7 -8
- localstack/utils/config_listener.py +1 -1
- localstack/utils/container_networking.py +2 -3
- localstack/utils/container_utils/container_client.py +135 -136
- localstack/utils/container_utils/docker_cmd_client.py +85 -69
- localstack/utils/container_utils/docker_sdk_client.py +69 -66
- localstack/utils/crypto.py +10 -10
- localstack/utils/diagnose.py +3 -4
- localstack/utils/docker_utils.py +9 -5
- localstack/utils/files.py +33 -13
- localstack/utils/functions.py +4 -3
- localstack/utils/http.py +11 -11
- localstack/utils/json.py +20 -6
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +15 -9
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +9 -2
- localstack/utils/objects.py +7 -6
- localstack/utils/patch.py +10 -3
- localstack/utils/run.py +12 -11
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +3 -4
- localstack/utils/strings.py +15 -16
- localstack/utils/sync.py +126 -1
- localstack/utils/tagging.py +8 -6
- localstack/utils/testutil.py +8 -8
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +12 -4
- localstack/utils/urls.py +1 -3
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +16 -3
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
- localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
- localstack/packages/terraform.py +0 -46
- localstack/services/cloudformation/deploy.html +0 -144
- localstack/services/cloudformation/deploy_ui.py +0 -47
- localstack/services/cloudformation/plugins.py +0 -12
- localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import logging
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
3
5
|
import uuid
|
|
6
|
+
from collections.abc import Callable
|
|
4
7
|
from dataclasses import dataclass
|
|
5
8
|
from datetime import UTC, datetime
|
|
6
|
-
from typing import Final, Protocol
|
|
9
|
+
from typing import Final, Protocol, TypeVar
|
|
7
10
|
|
|
8
11
|
from localstack import config
|
|
9
12
|
from localstack.aws.api.cloudformation import (
|
|
@@ -15,17 +18,14 @@ from localstack.aws.api.cloudformation import (
|
|
|
15
18
|
from localstack.constants import INTERNAL_AWS_SECRET_ACCESS_KEY
|
|
16
19
|
from localstack.services.cloudformation.analytics import track_resource_operation
|
|
17
20
|
from localstack.services.cloudformation.deployment_utils import log_not_available_message
|
|
18
|
-
from localstack.services.cloudformation.engine.template_deployer import REGEX_OUTPUT_APIGATEWAY
|
|
19
21
|
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
20
22
|
NodeDependsOn,
|
|
21
23
|
NodeOutput,
|
|
22
24
|
NodeResource,
|
|
23
|
-
TerminalValueCreated,
|
|
24
|
-
TerminalValueModified,
|
|
25
|
-
TerminalValueUnchanged,
|
|
26
25
|
is_nothing,
|
|
27
26
|
)
|
|
28
27
|
from localstack.services.cloudformation.engine.v2.change_set_model_preproc import (
|
|
28
|
+
_AWS_URL_SUFFIX,
|
|
29
29
|
MOCKED_REFERENCE,
|
|
30
30
|
ChangeSetModelPreproc,
|
|
31
31
|
PreprocEntityDelta,
|
|
@@ -41,28 +41,50 @@ from localstack.services.cloudformation.resource_provider import (
|
|
|
41
41
|
ResourceProviderPayload,
|
|
42
42
|
)
|
|
43
43
|
from localstack.services.cloudformation.v2.entities import ChangeSet, ResolvedResource
|
|
44
|
-
from localstack.utils.urls import localstack_host
|
|
45
44
|
|
|
46
45
|
LOG = logging.getLogger(__name__)
|
|
47
46
|
|
|
48
47
|
EventOperationFromAction = {"Add": "CREATE", "Modify": "UPDATE", "Remove": "DELETE"}
|
|
49
48
|
|
|
49
|
+
REGEX_OUTPUT_APIGATEWAY = re.compile(
|
|
50
|
+
rf"^(https?://.+\.execute-api\.)(?:[^-]+-){{2,3}}\d\.(amazonaws\.com|{_AWS_URL_SUFFIX})/?(.*)$"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
_T = TypeVar("_T")
|
|
54
|
+
|
|
50
55
|
|
|
51
56
|
@dataclass
|
|
52
57
|
class ChangeSetModelExecutorResult:
|
|
53
58
|
resources: dict[str, ResolvedResource]
|
|
54
59
|
outputs: list[Output]
|
|
60
|
+
failure_message: str | None = None
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
class DeferredAction(Protocol):
|
|
58
64
|
def __call__(self) -> None: ...
|
|
59
65
|
|
|
60
66
|
|
|
67
|
+
@dataclass
|
|
68
|
+
class Deferred:
|
|
69
|
+
name: str
|
|
70
|
+
action: DeferredAction
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TriggerRollback(Exception):
|
|
74
|
+
"""
|
|
75
|
+
Sentinel exception to signal that the deployment should be stopped for a reason
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, logical_resource_id: str, reason: str | None):
|
|
79
|
+
self.logical_resource_id = logical_resource_id
|
|
80
|
+
self.reason = reason
|
|
81
|
+
|
|
82
|
+
|
|
61
83
|
class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
62
84
|
# TODO: add typing for resolved resources and parameters.
|
|
63
85
|
resources: Final[dict[str, ResolvedResource]]
|
|
64
86
|
outputs: Final[list[Output]]
|
|
65
|
-
_deferred_actions: list[
|
|
87
|
+
_deferred_actions: list[Deferred]
|
|
66
88
|
|
|
67
89
|
def __init__(self, change_set: ChangeSet):
|
|
68
90
|
super().__init__(change_set=change_set)
|
|
@@ -76,24 +98,42 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
76
98
|
|
|
77
99
|
def execute(self) -> ChangeSetModelExecutorResult:
|
|
78
100
|
# constructive process
|
|
79
|
-
|
|
101
|
+
failure_message = None
|
|
102
|
+
try:
|
|
103
|
+
self.process()
|
|
104
|
+
except TriggerRollback as e:
|
|
105
|
+
failure_message = e.reason
|
|
106
|
+
except Exception as e:
|
|
107
|
+
failure_message = str(e)
|
|
80
108
|
|
|
109
|
+
is_deletion = self._change_set.stack.status == StackStatus.DELETE_IN_PROGRESS
|
|
81
110
|
if self._deferred_actions:
|
|
82
|
-
|
|
111
|
+
if not is_deletion:
|
|
112
|
+
# TODO: correct status
|
|
113
|
+
# TODO: differentiate between update and create
|
|
114
|
+
self._change_set.stack.set_stack_status(
|
|
115
|
+
StackStatus.ROLLBACK_IN_PROGRESS
|
|
116
|
+
if failure_message
|
|
117
|
+
else StackStatus.UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
|
|
118
|
+
)
|
|
83
119
|
|
|
84
120
|
# perform all deferred actions such as deletions. These must happen in reverse from their
|
|
85
121
|
# defined order so that resource dependencies are honoured
|
|
86
122
|
# TODO: errors will stop all rollbacks; get parity on this behaviour
|
|
87
|
-
for
|
|
88
|
-
action
|
|
123
|
+
for deferred in self._deferred_actions[::-1]:
|
|
124
|
+
LOG.debug("executing deferred action: '%s'", deferred.name)
|
|
125
|
+
deferred.action()
|
|
126
|
+
|
|
127
|
+
if failure_message and not is_deletion:
|
|
128
|
+
# TODO: differentiate between update and create
|
|
129
|
+
self._change_set.stack.set_stack_status(StackStatus.ROLLBACK_COMPLETE)
|
|
89
130
|
|
|
90
131
|
return ChangeSetModelExecutorResult(
|
|
91
|
-
resources=self.resources,
|
|
92
|
-
outputs=self.outputs,
|
|
132
|
+
resources=self.resources, outputs=self.outputs, failure_message=failure_message
|
|
93
133
|
)
|
|
94
134
|
|
|
95
|
-
def _defer_action(self, action: DeferredAction):
|
|
96
|
-
self._deferred_actions.append(action)
|
|
135
|
+
def _defer_action(self, name: str, action: DeferredAction):
|
|
136
|
+
self._deferred_actions.append(Deferred(name=name, action=action))
|
|
97
137
|
|
|
98
138
|
def _get_physical_id(self, logical_resource_id, strict: bool = True) -> str | None:
|
|
99
139
|
physical_resource_id = None
|
|
@@ -129,9 +169,10 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
129
169
|
else:
|
|
130
170
|
status = f"{status_from_action}_{event_status.name}"
|
|
131
171
|
|
|
172
|
+
physical_resource_id = self._get_physical_id(logical_resource_id, False)
|
|
132
173
|
self._change_set.stack.set_resource_status(
|
|
133
174
|
logical_resource_id=logical_resource_id,
|
|
134
|
-
physical_resource_id=
|
|
175
|
+
physical_resource_id=physical_resource_id,
|
|
135
176
|
resource_type=resource_type,
|
|
136
177
|
status=ResourceStatus(status),
|
|
137
178
|
resource_status_reason=reason,
|
|
@@ -184,6 +225,12 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
184
225
|
try:
|
|
185
226
|
delta = super().visit_node_resource(node_resource=node_resource)
|
|
186
227
|
except Exception as e:
|
|
228
|
+
LOG.debug(
|
|
229
|
+
"preprocessing resource '%s' failed: %s",
|
|
230
|
+
node_resource.name,
|
|
231
|
+
e,
|
|
232
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG) and config.CFN_VERBOSE_ERRORS,
|
|
233
|
+
)
|
|
187
234
|
self._process_event(
|
|
188
235
|
action=node_resource.change_type.to_change_action(),
|
|
189
236
|
logical_resource_id=node_resource.name,
|
|
@@ -211,10 +258,24 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
211
258
|
# Update the latest version of this resource for downstream references.
|
|
212
259
|
if not is_nothing(after):
|
|
213
260
|
after_logical_id = after.logical_id
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
261
|
+
resource = self.resources[after_logical_id]
|
|
262
|
+
resource_failed_to_deploy = resource["ResourceStatus"] in {
|
|
263
|
+
ResourceStatus.CREATE_FAILED,
|
|
264
|
+
ResourceStatus.UPDATE_FAILED,
|
|
265
|
+
}
|
|
266
|
+
if not resource_failed_to_deploy:
|
|
267
|
+
after_physical_id: str = self._after_resource_physical_id(
|
|
268
|
+
resource_logical_id=after_logical_id
|
|
269
|
+
)
|
|
270
|
+
after.physical_resource_id = after_physical_id
|
|
271
|
+
after.status = resource["ResourceStatus"]
|
|
272
|
+
|
|
273
|
+
# terminate the deployment process
|
|
274
|
+
if resource_failed_to_deploy:
|
|
275
|
+
raise TriggerRollback(
|
|
276
|
+
logical_resource_id=after_logical_id,
|
|
277
|
+
reason=resource.get("ResourceStatusReason"),
|
|
278
|
+
)
|
|
218
279
|
return delta
|
|
219
280
|
|
|
220
281
|
def visit_node_output(
|
|
@@ -283,6 +344,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
283
344
|
resource_type=before.resource_type,
|
|
284
345
|
before_properties=before_properties,
|
|
285
346
|
after_properties=None,
|
|
347
|
+
part_of_replacement=True,
|
|
286
348
|
)
|
|
287
349
|
self._process_event(
|
|
288
350
|
action=ChangeAction.Remove,
|
|
@@ -292,7 +354,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
292
354
|
reason=event.message,
|
|
293
355
|
)
|
|
294
356
|
|
|
295
|
-
self._defer_action(cleanup)
|
|
357
|
+
self._defer_action(f"cleanup-from-replacement-{name}", cleanup)
|
|
296
358
|
else:
|
|
297
359
|
event = self._execute_resource_action(
|
|
298
360
|
action=ChangeAction.Modify,
|
|
@@ -331,7 +393,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
331
393
|
reason=event.message,
|
|
332
394
|
)
|
|
333
395
|
|
|
334
|
-
self._defer_action(perform_deletion)
|
|
396
|
+
self._defer_action(f"type-migration-{name}", perform_deletion)
|
|
335
397
|
|
|
336
398
|
event = self._execute_resource_action(
|
|
337
399
|
action=ChangeAction.Add,
|
|
@@ -375,7 +437,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
375
437
|
reason=event.message,
|
|
376
438
|
)
|
|
377
439
|
|
|
378
|
-
self._defer_action(perform_deletion)
|
|
440
|
+
self._defer_action(f"remove-{name}", perform_deletion)
|
|
379
441
|
elif not is_nothing(after):
|
|
380
442
|
# Case: addition
|
|
381
443
|
self._process_event(
|
|
@@ -417,6 +479,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
417
479
|
resource_type: str,
|
|
418
480
|
before_properties: PreprocProperties | None,
|
|
419
481
|
after_properties: PreprocProperties | None,
|
|
482
|
+
part_of_replacement: bool = False,
|
|
420
483
|
) -> ProgressEvent:
|
|
421
484
|
LOG.debug("Executing resource action: %s for resource '%s'", action, logical_resource_id)
|
|
422
485
|
payload = self.create_resource_provider_payload(
|
|
@@ -454,9 +517,11 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
454
517
|
resource_type,
|
|
455
518
|
f'No resource provider found for "{resource_type}"',
|
|
456
519
|
)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
520
|
+
if "CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES" not in os.environ:
|
|
521
|
+
LOG.warning(
|
|
522
|
+
"Deployment of resource type %s succeeded, but will fail in upcoming LocalStack releases unless CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES is explicitly enabled.",
|
|
523
|
+
resource_type,
|
|
524
|
+
)
|
|
460
525
|
event = ProgressEvent(
|
|
461
526
|
OperationStatus.SUCCESS,
|
|
462
527
|
resource_model={},
|
|
@@ -473,6 +538,19 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
473
538
|
message=f"Resource type {resource_type} not supported",
|
|
474
539
|
)
|
|
475
540
|
|
|
541
|
+
if part_of_replacement and action == ChangeAction.Remove:
|
|
542
|
+
# Early return as we don't want to update internal state of the executor if this is a
|
|
543
|
+
# cleanup of an old resource. The new resource has already been created and the state
|
|
544
|
+
# updated
|
|
545
|
+
return event
|
|
546
|
+
|
|
547
|
+
status_from_action = EventOperationFromAction[action.value]
|
|
548
|
+
resolved_resource = ResolvedResource(
|
|
549
|
+
Properties=event.resource_model,
|
|
550
|
+
LogicalResourceId=logical_resource_id,
|
|
551
|
+
Type=resource_type,
|
|
552
|
+
LastUpdatedTimestamp=datetime.now(UTC),
|
|
553
|
+
)
|
|
476
554
|
match event.status:
|
|
477
555
|
case OperationStatus.SUCCESS:
|
|
478
556
|
# merge the resources state with the external state
|
|
@@ -487,33 +565,29 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
487
565
|
|
|
488
566
|
# Don't update the resolved resources if we have deleted that resource
|
|
489
567
|
if action != ChangeAction.Remove:
|
|
490
|
-
status_from_action = EventOperationFromAction[action.value]
|
|
491
568
|
physical_resource_id = (
|
|
492
569
|
extra_resource_properties["PhysicalResourceId"]
|
|
493
570
|
if resource_provider
|
|
494
571
|
else MOCKED_REFERENCE
|
|
495
572
|
)
|
|
496
|
-
resolved_resource =
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
Type=resource_type,
|
|
500
|
-
LastUpdatedTimestamp=datetime.now(UTC),
|
|
501
|
-
ResourceStatus=ResourceStatus(f"{status_from_action}_COMPLETE"),
|
|
502
|
-
PhysicalResourceId=physical_resource_id,
|
|
573
|
+
resolved_resource["PhysicalResourceId"] = physical_resource_id
|
|
574
|
+
resolved_resource["ResourceStatus"] = ResourceStatus(
|
|
575
|
+
f"{status_from_action}_COMPLETE"
|
|
503
576
|
)
|
|
504
577
|
# TODO: do we actually need this line?
|
|
505
578
|
resolved_resource.update(extra_resource_properties)
|
|
506
|
-
|
|
507
|
-
self.resources[logical_resource_id] = resolved_resource
|
|
508
|
-
|
|
509
579
|
case OperationStatus.FAILED:
|
|
510
580
|
reason = event.message
|
|
511
581
|
LOG.warning(
|
|
512
582
|
"Resource provider operation failed: '%s'",
|
|
513
583
|
reason,
|
|
514
584
|
)
|
|
585
|
+
resolved_resource["ResourceStatus"] = ResourceStatus(f"{status_from_action}_FAILED")
|
|
586
|
+
resolved_resource["ResourceStatusReason"] = reason
|
|
515
587
|
case other:
|
|
516
588
|
raise NotImplementedError(f"Event status '{other}' not handled")
|
|
589
|
+
|
|
590
|
+
self.resources[logical_resource_id] = resolved_resource
|
|
517
591
|
return event
|
|
518
592
|
|
|
519
593
|
def create_resource_provider_payload(
|
|
@@ -572,43 +646,10 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
|
|
|
572
646
|
}
|
|
573
647
|
return resource_provider_payload
|
|
574
648
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
api_match = REGEX_OUTPUT_APIGATEWAY.match(value)
|
|
578
|
-
if api_match and value not in config.CFN_STRING_REPLACEMENT_DENY_LIST:
|
|
579
|
-
prefix = api_match[1]
|
|
580
|
-
host = api_match[2]
|
|
581
|
-
path = api_match[3]
|
|
582
|
-
port = localstack_host().port
|
|
583
|
-
value = f"{prefix}{host}:{port}/{path}"
|
|
584
|
-
return value
|
|
585
|
-
|
|
586
|
-
return value
|
|
587
|
-
|
|
588
|
-
def visit_terminal_value_created(
|
|
589
|
-
self, value: TerminalValueCreated
|
|
590
|
-
) -> PreprocEntityDelta[str, str]:
|
|
591
|
-
if isinstance(value.value, str):
|
|
592
|
-
after = self._replace_url_outputs_if_required(value.value)
|
|
593
|
-
else:
|
|
594
|
-
after = value.value
|
|
595
|
-
return PreprocEntityDelta(after=after)
|
|
596
|
-
|
|
597
|
-
def visit_terminal_value_modified(
|
|
598
|
-
self, value: TerminalValueModified
|
|
599
|
-
) -> PreprocEntityDelta[str, str]:
|
|
600
|
-
# we only need to transform the after
|
|
601
|
-
if isinstance(value.modified_value, str):
|
|
602
|
-
after = self._replace_url_outputs_if_required(value.modified_value)
|
|
603
|
-
else:
|
|
604
|
-
after = value.modified_value
|
|
605
|
-
return PreprocEntityDelta(before=value.value, after=after)
|
|
606
|
-
|
|
607
|
-
def visit_terminal_value_unchanged(
|
|
608
|
-
self, terminal_value_unchanged: TerminalValueUnchanged
|
|
649
|
+
def _maybe_perform_on_delta(
|
|
650
|
+
self, delta: PreprocEntityDelta, f: Callable[[_T], _T]
|
|
609
651
|
) -> PreprocEntityDelta:
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
return PreprocEntityDelta(before=value, after=value)
|
|
652
|
+
# we only care about the after state
|
|
653
|
+
if isinstance(delta.after, str):
|
|
654
|
+
delta.after = f(delta.after)
|
|
655
|
+
return delta
|