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
|
@@ -60,7 +60,7 @@ class ServiceRequestCounter:
|
|
|
60
60
|
if context.service_exception:
|
|
61
61
|
return context.service_exception.code
|
|
62
62
|
|
|
63
|
-
response = parse_response(context.operation, response)
|
|
63
|
+
response = parse_response(context.operation, context.protocol, response)
|
|
64
64
|
return response["Error"]["Code"]
|
|
65
65
|
except Exception:
|
|
66
66
|
if config.DEBUG_ANALYTICS:
|
|
@@ -20,7 +20,12 @@ class InternalRequestParamsEnricher(Handler):
|
|
|
20
20
|
try:
|
|
21
21
|
dto = MappingProxyType(load_dto(header))
|
|
22
22
|
except Exception as e:
|
|
23
|
-
LOG.
|
|
23
|
+
LOG.error(
|
|
24
|
+
"Error loading request parameters '%s', Error: %s",
|
|
25
|
+
header,
|
|
26
|
+
e,
|
|
27
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
28
|
+
)
|
|
24
29
|
return
|
|
25
30
|
|
|
26
31
|
context.internal_request_params = dto
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Handlers for logging."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
import types
|
|
4
5
|
from functools import cached_property
|
|
5
6
|
|
|
6
7
|
from localstack.aws.api import RequestContext, ServiceException
|
|
@@ -21,6 +22,14 @@ class ExceptionLogger(ExceptionHandler):
|
|
|
21
22
|
def __init__(self, logger=None):
|
|
22
23
|
self.logger = logger or LOG
|
|
23
24
|
|
|
25
|
+
try:
|
|
26
|
+
import moto.core.exceptions
|
|
27
|
+
|
|
28
|
+
self._moto_service_exception = moto.core.exceptions.ServiceException
|
|
29
|
+
except (ModuleNotFoundError, AttributeError):
|
|
30
|
+
# Moto may not be available in stripped-down versions of LocalStack, like LocalStack S3 image.
|
|
31
|
+
self._moto_service_exception = types.EllipsisType
|
|
32
|
+
|
|
24
33
|
def __call__(
|
|
25
34
|
self,
|
|
26
35
|
chain: HandlerChain,
|
|
@@ -28,9 +37,10 @@ class ExceptionLogger(ExceptionHandler):
|
|
|
28
37
|
context: RequestContext,
|
|
29
38
|
response: Response,
|
|
30
39
|
):
|
|
31
|
-
if isinstance(exception, ServiceException):
|
|
40
|
+
if isinstance(exception, (ServiceException, self._moto_service_exception)):
|
|
32
41
|
# We do not want to log an error/stacktrace if the handler is working as expected, but chooses to throw
|
|
33
|
-
# a service exception
|
|
42
|
+
# a service exception. It may also throw a Moto ServiceException, which should not be logged either
|
|
43
|
+
# because ServiceExceptionHandler understands it.
|
|
34
44
|
return
|
|
35
45
|
if self.logger.isEnabledFor(level=logging.DEBUG):
|
|
36
46
|
self.logger.exception("exception during call chain", exc_info=exception)
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
import csv
|
|
1
2
|
import logging
|
|
3
|
+
import os
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
2
6
|
|
|
3
7
|
from localstack import config
|
|
4
8
|
from localstack.aws.api import RequestContext
|
|
5
9
|
from localstack.aws.chain import HandlerChain
|
|
10
|
+
from localstack.constants import ENV_INTERNAL_TEST_STORE_METRICS_PATH
|
|
6
11
|
from localstack.http import Response
|
|
12
|
+
from localstack.utils.strings import short_uid
|
|
7
13
|
|
|
8
14
|
LOG = logging.getLogger(__name__)
|
|
9
15
|
|
|
@@ -137,6 +143,32 @@ class MetricHandler:
|
|
|
137
143
|
|
|
138
144
|
def __init__(self) -> None:
|
|
139
145
|
self.metrics_handler_items = {}
|
|
146
|
+
self.local_filename = None
|
|
147
|
+
|
|
148
|
+
if self.should_store_metric_locally():
|
|
149
|
+
self.local_filename = self.create_local_file()
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def should_store_metric_locally() -> bool:
|
|
153
|
+
return config.is_collect_metrics_mode() and config.store_test_metrics_in_local_filesystem()
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def create_local_file():
|
|
157
|
+
folder = Path(
|
|
158
|
+
os.environ.get(ENV_INTERNAL_TEST_STORE_METRICS_PATH, "/tmp/localstack-metrics")
|
|
159
|
+
)
|
|
160
|
+
if not folder.exists():
|
|
161
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
LOG.debug("Metric reports will be stored in %s", folder)
|
|
163
|
+
filename = (
|
|
164
|
+
folder
|
|
165
|
+
/ f"metric-report-raw-data-{datetime.utcnow().strftime('%Y-%m-%d__%H_%M_%S')}-{short_uid()}.csv"
|
|
166
|
+
)
|
|
167
|
+
with open(filename, "w") as fd:
|
|
168
|
+
LOG.debug("Creating new metric data file %s", filename)
|
|
169
|
+
writer = csv.writer(fd)
|
|
170
|
+
writer.writerow(Metric.RAW_DATA_HEADER)
|
|
171
|
+
return filename
|
|
140
172
|
|
|
141
173
|
def create_metric_handler_item(
|
|
142
174
|
self, chain: HandlerChain, context: RequestContext, response: Response
|
|
@@ -194,7 +226,15 @@ class MetricHandler:
|
|
|
194
226
|
)
|
|
195
227
|
# refrain from adding duplicates
|
|
196
228
|
if metric not in MetricHandler.metric_data:
|
|
197
|
-
|
|
229
|
+
self.append_metric(metric)
|
|
198
230
|
|
|
199
231
|
# cleanup
|
|
200
232
|
del self.metrics_handler_items[context]
|
|
233
|
+
|
|
234
|
+
def append_metric(self, metric: Metric):
|
|
235
|
+
if self.should_store_metric_locally():
|
|
236
|
+
with open(self.local_filename, "a") as fd:
|
|
237
|
+
writer = csv.writer(fd)
|
|
238
|
+
writer.writerow(metric)
|
|
239
|
+
else:
|
|
240
|
+
MetricHandler.metric_data.append(metric)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import traceback
|
|
5
|
+
import types
|
|
5
6
|
from collections import defaultdict
|
|
6
7
|
from typing import Any
|
|
7
8
|
|
|
@@ -9,15 +10,15 @@ from botocore.model import OperationModel, ServiceModel
|
|
|
9
10
|
|
|
10
11
|
from localstack import config
|
|
11
12
|
from localstack.http import Response
|
|
12
|
-
from localstack.utils.coverage_docs import get_coverage_link_for_service
|
|
13
13
|
|
|
14
|
+
from ...utils.coverage_docs import get_coverage_link_for_service
|
|
14
15
|
from ..api import CommonServiceException, RequestContext, ServiceException
|
|
15
16
|
from ..api.core import ServiceOperation
|
|
16
17
|
from ..chain import CompositeResponseHandler, ExceptionHandler, Handler, HandlerChain
|
|
17
18
|
from ..client import parse_response, parse_service_exception
|
|
18
19
|
from ..protocol.parser import RequestParser, create_parser
|
|
19
20
|
from ..protocol.serializer import create_serializer
|
|
20
|
-
from ..protocol.service_router import determine_aws_service_model
|
|
21
|
+
from ..protocol.service_router import determine_aws_protocol, determine_aws_service_model
|
|
21
22
|
from ..skeleton import Skeleton, create_skeleton
|
|
22
23
|
|
|
23
24
|
LOG = logging.getLogger(__name__)
|
|
@@ -33,6 +34,8 @@ class ServiceNameParser(Handler):
|
|
|
33
34
|
# example). If it is already set, we can skip the parsing of the request. It is very important for S3, because
|
|
34
35
|
# parsing the request will consume the data stream and prevent streaming.
|
|
35
36
|
if context.service:
|
|
37
|
+
if not context.protocol:
|
|
38
|
+
context.protocol = determine_aws_protocol(context.request, context.service)
|
|
36
39
|
return
|
|
37
40
|
|
|
38
41
|
service_model = determine_aws_service_model(context.request)
|
|
@@ -41,6 +44,7 @@ class ServiceNameParser(Handler):
|
|
|
41
44
|
return
|
|
42
45
|
|
|
43
46
|
context.service = service_model
|
|
47
|
+
context.protocol = determine_aws_protocol(context.request, service_model)
|
|
44
48
|
|
|
45
49
|
|
|
46
50
|
class ServiceRequestParser(Handler):
|
|
@@ -63,7 +67,7 @@ class ServiceRequestParser(Handler):
|
|
|
63
67
|
return self.parse_and_enrich(context)
|
|
64
68
|
|
|
65
69
|
def parse_and_enrich(self, context: RequestContext):
|
|
66
|
-
parser = create_parser(context.service)
|
|
70
|
+
parser = create_parser(context.service, context.protocol)
|
|
67
71
|
operation, instance = parser.parse(context.request)
|
|
68
72
|
|
|
69
73
|
# enrich context
|
|
@@ -137,7 +141,7 @@ class ServiceRequestRouter(Handler):
|
|
|
137
141
|
operation_name = operation.name
|
|
138
142
|
message = f"no handler for operation '{operation_name}' on service '{service_name}'"
|
|
139
143
|
error = CommonServiceException("InternalFailure", message, status_code=501)
|
|
140
|
-
serializer = create_serializer(context.service)
|
|
144
|
+
serializer = create_serializer(context.service, context.protocol)
|
|
141
145
|
return serializer.serialize_error_to_response(
|
|
142
146
|
error, operation, context.request.headers, context.request_id
|
|
143
147
|
)
|
|
@@ -153,6 +157,15 @@ class ServiceExceptionSerializer(ExceptionHandler):
|
|
|
153
157
|
def __init__(self):
|
|
154
158
|
self.handle_internal_failures = True
|
|
155
159
|
|
|
160
|
+
try:
|
|
161
|
+
import moto.core.exceptions
|
|
162
|
+
|
|
163
|
+
self._moto_service_exception = moto.core.exceptions.ServiceException
|
|
164
|
+
except (ModuleNotFoundError, AttributeError) as exc:
|
|
165
|
+
# Moto may not be available in stripped-down versions of LocalStack, like LocalStack S3 image.
|
|
166
|
+
LOG.debug("Unable to set up Moto ServiceException translation: %s", exc)
|
|
167
|
+
self._moto_service_exception = types.EllipsisType
|
|
168
|
+
|
|
156
169
|
def __call__(
|
|
157
170
|
self,
|
|
158
171
|
chain: HandlerChain,
|
|
@@ -176,43 +189,48 @@ class ServiceExceptionSerializer(ExceptionHandler):
|
|
|
176
189
|
action_name = operation.name
|
|
177
190
|
exception_message: str | None = exception.args[0] if exception.args else None
|
|
178
191
|
message = exception_message or get_coverage_link_for_service(service_name, action_name)
|
|
179
|
-
LOG.info(message)
|
|
180
192
|
error = CommonServiceException("InternalFailure", message, status_code=501)
|
|
181
|
-
|
|
193
|
+
LOG.info(message)
|
|
194
|
+
|
|
195
|
+
elif isinstance(exception, self._moto_service_exception):
|
|
196
|
+
# Translate Moto ServiceException to native ServiceException if Moto is available.
|
|
197
|
+
# This allows handler chain to gracefully handles Moto errors when provider handlers invoke Moto methods directly.
|
|
198
|
+
error = CommonServiceException(
|
|
199
|
+
code=exception.code,
|
|
200
|
+
message=exception.message,
|
|
201
|
+
)
|
|
182
202
|
|
|
183
203
|
elif not isinstance(exception, ServiceException):
|
|
184
204
|
if not self.handle_internal_failures:
|
|
185
205
|
return
|
|
186
206
|
|
|
187
207
|
if config.INCLUDE_STACK_TRACES_IN_HTTP_RESPONSE:
|
|
188
|
-
|
|
208
|
+
message = "".join(
|
|
189
209
|
traceback.format_exception(
|
|
190
210
|
type(exception), value=exception, tb=exception.__traceback__
|
|
191
211
|
)
|
|
192
212
|
)
|
|
213
|
+
else:
|
|
214
|
+
message = str(exception)
|
|
193
215
|
|
|
194
216
|
# wrap exception for serialization
|
|
195
217
|
if operation:
|
|
196
218
|
operation_name = operation.name
|
|
197
|
-
msg = "exception while calling
|
|
198
|
-
service_name,
|
|
199
|
-
operation_name,
|
|
200
|
-
exception,
|
|
201
|
-
)
|
|
219
|
+
msg = f"exception while calling {service_name}.{operation_name}: {message}"
|
|
202
220
|
else:
|
|
203
221
|
# just use any operation for mocking purposes (the parser needs it to populate the default response)
|
|
204
222
|
operation = context.service.operation_model(context.service.operation_names[0])
|
|
205
|
-
msg = "exception while calling
|
|
206
|
-
service_name,
|
|
207
|
-
exception,
|
|
208
|
-
)
|
|
223
|
+
msg = f"exception while calling {service_name} with unknown operation: {message}"
|
|
209
224
|
|
|
210
225
|
status_code = 501 if config.FAIL_FAST else 500
|
|
211
226
|
|
|
212
|
-
error = CommonServiceException(
|
|
213
|
-
|
|
227
|
+
error = CommonServiceException(
|
|
228
|
+
"InternalError", msg, status_code=status_code
|
|
229
|
+
).with_traceback(exception.__traceback__)
|
|
214
230
|
|
|
215
|
-
|
|
231
|
+
context.service_exception = error
|
|
232
|
+
|
|
233
|
+
serializer = create_serializer(context.service, context.protocol)
|
|
216
234
|
return serializer.serialize_error_to_response(
|
|
217
235
|
error, operation, context.request.headers, context.request_id
|
|
218
236
|
)
|
|
@@ -255,7 +273,9 @@ class ServiceResponseParser(Handler):
|
|
|
255
273
|
return
|
|
256
274
|
|
|
257
275
|
# in this case we need to parse the raw response
|
|
258
|
-
parsed = parse_response(
|
|
276
|
+
parsed = parse_response(
|
|
277
|
+
context.operation, context.protocol, response, include_response_metadata=False
|
|
278
|
+
)
|
|
259
279
|
if service_exception := parse_service_exception(response, parsed):
|
|
260
280
|
context.service_exception = service_exception
|
|
261
281
|
else:
|
localstack/aws/mocking.py
CHANGED
|
@@ -224,7 +224,7 @@ custom_arns = {
|
|
|
224
224
|
def generate_instance(shape: Shape, graph: ShapeGraph) -> Instance | None:
|
|
225
225
|
if shape is None:
|
|
226
226
|
return None
|
|
227
|
-
raise ValueError("could not generate shape for type
|
|
227
|
+
raise ValueError(f"could not generate shape for type {shape.type_name}")
|
|
228
228
|
|
|
229
229
|
|
|
230
230
|
@generate_instance.register
|
|
@@ -383,7 +383,7 @@ def _(shape: Shape, graph: ShapeGraph) -> int | float | bool | bytes | date:
|
|
|
383
383
|
if shape.type_name == "timestamp":
|
|
384
384
|
return datetime.now()
|
|
385
385
|
|
|
386
|
-
raise ValueError("unknown type
|
|
386
|
+
raise ValueError(f"unknown type {shape.type_name}")
|
|
387
387
|
|
|
388
388
|
|
|
389
389
|
def generate_response(operation: OperationModel):
|
localstack/aws/patches.py
CHANGED
|
@@ -20,12 +20,12 @@ def patch_moto_instance_tracker_meta():
|
|
|
20
20
|
cls = super(InstanceTrackerMeta, meta).__new__(meta, name, bases, dct)
|
|
21
21
|
if name == "BaseModel":
|
|
22
22
|
return cls
|
|
23
|
-
cls.
|
|
23
|
+
cls.instances_tracked = []
|
|
24
24
|
return cls
|
|
25
25
|
|
|
26
26
|
@patch(BaseModel.__new__, pass_target=False)
|
|
27
27
|
def new_basemodel(cls, *args, **kwargs):
|
|
28
|
-
# skip cls.
|
|
28
|
+
# skip cls.instances_tracked.append(..) which is done by the original/upstream constructor
|
|
29
29
|
instance = super(BaseModel, cls).__new__(cls)
|
|
30
30
|
return instance
|
|
31
31
|
|