localstack-core 4.7.1.dev95__py3-none-any.whl → 4.7.1.dev97__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.
Potentially problematic release.
This version of localstack-core might be problematic. Click here for more details.
- localstack/aws/mocking.py +2 -2
- localstack/aws/protocol/parser.py +6 -6
- localstack/aws/protocol/serializer.py +5 -5
- localstack/aws/scaffold.py +1 -1
- localstack/cli/localstack.py +1 -1
- localstack/cli/plugins.py +1 -1
- localstack/config.py +5 -5
- localstack/dev/run/configurators.py +1 -4
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +1 -1
- localstack/runtime/main.py +5 -5
- 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 +2 -2
- localstack/services/apigateway/legacy/provider.py +4 -4
- 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 +1 -2
- localstack/services/cloudformation/engine/template_deployer.py +2 -2
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +2 -2
- localstack/services/cloudformation/provider.py +2 -2
- localstack/services/cloudformation/service_models.py +1 -1
- localstack/services/dynamodb/server.py +1 -1
- localstack/services/edge.py +1 -1
- localstack/services/events/v1/provider.py +5 -5
- localstack/services/kinesis/provider.py +1 -1
- localstack/services/plugins.py +6 -6
- localstack/services/sqs/constants.py +1 -1
- localstack/services/sqs/provider.py +18 -0
- localstack/services/ssm/provider.py +1 -1
- localstack/state/pickle.py +1 -1
- localstack/testing/pytest/fixtures.py +3 -3
- localstack/utils/analytics/metadata.py +1 -1
- localstack/utils/aws/arns.py +7 -20
- localstack/utils/aws/aws_responses.py +1 -1
- localstack/utils/aws/dead_letter_queue.py +1 -5
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +1 -1
- localstack/utils/bootstrap.py +4 -4
- localstack/utils/container_utils/docker_cmd_client.py +19 -19
- localstack/utils/crypto.py +10 -10
- localstack/utils/diagnose.py +1 -1
- localstack/utils/files.py +2 -6
- localstack/utils/functions.py +1 -1
- localstack/utils/http.py +5 -5
- localstack/utils/net.py +3 -2
- localstack/utils/objects.py +1 -1
- localstack/utils/run.py +2 -2
- localstack/utils/serving.py +1 -1
- localstack/utils/strings.py +5 -5
- localstack/utils/testutil.py +3 -4
- localstack/utils/time.py +1 -1
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +2 -2
- {localstack_core-4.7.1.dev95.dist-info → localstack_core-4.7.1.dev97.dist-info}/METADATA +1 -1
- {localstack_core-4.7.1.dev95.dist-info → localstack_core-4.7.1.dev97.dist-info}/RECORD +66 -66
- localstack_core-4.7.1.dev97.dist-info/plux.json +1 -0
- localstack_core-4.7.1.dev95.dist-info/plux.json +0 -1
- {localstack_core-4.7.1.dev95.data → localstack_core-4.7.1.dev97.data}/scripts/localstack +0 -0
- {localstack_core-4.7.1.dev95.data → localstack_core-4.7.1.dev97.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.7.1.dev95.data → localstack_core-4.7.1.dev97.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.7.1.dev95.dist-info → localstack_core-4.7.1.dev97.dist-info}/WHEEL +0 -0
- {localstack_core-4.7.1.dev95.dist-info → localstack_core-4.7.1.dev97.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.7.1.dev95.dist-info → localstack_core-4.7.1.dev97.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.7.1.dev95.dist-info → localstack_core-4.7.1.dev97.dist-info}/top_level.txt +0 -0
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):
|
|
@@ -265,12 +265,12 @@ class RequestParser(abc.ABC):
|
|
|
265
265
|
if uri_param_name in uri_params:
|
|
266
266
|
payload = uri_params[uri_param_name]
|
|
267
267
|
else:
|
|
268
|
-
raise UnknownParserError("Unknown shape location '
|
|
268
|
+
raise UnknownParserError(f"Unknown shape location '{location}'.")
|
|
269
269
|
else:
|
|
270
270
|
# If we don't have to use a specific location, we use the node
|
|
271
271
|
payload = node
|
|
272
272
|
|
|
273
|
-
fn_name = "_parse_
|
|
273
|
+
fn_name = f"_parse_{shape.type_name}"
|
|
274
274
|
handler = getattr(self, fn_name, self._noop_parser)
|
|
275
275
|
try:
|
|
276
276
|
return handler(request, shape, payload, uri_params) if payload is not None else None
|
|
@@ -322,7 +322,7 @@ class RequestParser(abc.ABC):
|
|
|
322
322
|
return True
|
|
323
323
|
if value == "false":
|
|
324
324
|
return False
|
|
325
|
-
raise ValueError("cannot parse boolean value
|
|
325
|
+
raise ValueError(f"cannot parse boolean value {node}")
|
|
326
326
|
|
|
327
327
|
@_text_content
|
|
328
328
|
def _noop_parser(self, _, __, node: Any, ___):
|
|
@@ -336,7 +336,7 @@ class RequestParser(abc.ABC):
|
|
|
336
336
|
if timestamp_format is None:
|
|
337
337
|
timestamp_format = self.TIMESTAMP_FORMAT
|
|
338
338
|
timestamp_format = timestamp_format.lower()
|
|
339
|
-
converter = getattr(self, "_timestamp_
|
|
339
|
+
converter = getattr(self, f"_timestamp_{timestamp_format}")
|
|
340
340
|
final_value = converter(value)
|
|
341
341
|
return final_value
|
|
342
342
|
|
|
@@ -740,7 +740,7 @@ class RestXMLRequestParser(BaseRestRequestParser):
|
|
|
740
740
|
elif tag_name == value_location_name:
|
|
741
741
|
val_name = self._parse_shape(request, value_shape, single_pair, uri_params)
|
|
742
742
|
else:
|
|
743
|
-
raise ProtocolParserError("Unknown tag:
|
|
743
|
+
raise ProtocolParserError(f"Unknown tag: {tag_name}")
|
|
744
744
|
parsed[key_name] = val_name
|
|
745
745
|
return parsed
|
|
746
746
|
|
|
@@ -786,7 +786,7 @@ class RestXMLRequestParser(BaseRestRequestParser):
|
|
|
786
786
|
root = parser.close()
|
|
787
787
|
except ETree.ParseError as e:
|
|
788
788
|
raise ProtocolParserError(
|
|
789
|
-
"Unable to parse request (
|
|
789
|
+
f"Unable to parse request ({e}), invalid XML received:\n{xml_string}"
|
|
790
790
|
) from e
|
|
791
791
|
return root
|
|
792
792
|
|
|
@@ -527,7 +527,7 @@ class ResponseSerializer(abc.ABC):
|
|
|
527
527
|
timestamp_format = self.TIMESTAMP_FORMAT
|
|
528
528
|
timestamp_format = timestamp_format.lower()
|
|
529
529
|
datetime_obj = parse_to_aware_datetime(value)
|
|
530
|
-
converter = getattr(self, "_timestamp_
|
|
530
|
+
converter = getattr(self, f"_timestamp_{timestamp_format}")
|
|
531
531
|
final_value = converter(datetime_obj)
|
|
532
532
|
return final_value
|
|
533
533
|
|
|
@@ -689,7 +689,7 @@ class BaseXMLResponseSerializer(ResponseSerializer):
|
|
|
689
689
|
name = shape.serialization.get("resultWrapper")
|
|
690
690
|
|
|
691
691
|
try:
|
|
692
|
-
method = getattr(self, "_serialize_type_
|
|
692
|
+
method = getattr(self, f"_serialize_type_{shape.type_name}", self._default_serialize)
|
|
693
693
|
method(xmlnode, params, shape, name, mime_type)
|
|
694
694
|
except (TypeError, ValueError, AttributeError) as e:
|
|
695
695
|
raise ProtocolSerializerError(
|
|
@@ -705,7 +705,7 @@ class BaseXMLResponseSerializer(ResponseSerializer):
|
|
|
705
705
|
namespace_metadata = shape.serialization["xmlNamespace"]
|
|
706
706
|
attribute_name = "xmlns"
|
|
707
707
|
if namespace_metadata.get("prefix"):
|
|
708
|
-
attribute_name += "
|
|
708
|
+
attribute_name += ":{}".format(namespace_metadata["prefix"])
|
|
709
709
|
structure_node.attrib[attribute_name] = namespace_metadata["uri"]
|
|
710
710
|
for key, value in params.items():
|
|
711
711
|
if value is None:
|
|
@@ -1261,7 +1261,7 @@ class JSONResponseSerializer(ResponseSerializer):
|
|
|
1261
1261
|
else:
|
|
1262
1262
|
json_version = operation_model.metadata.get("jsonVersion")
|
|
1263
1263
|
if json_version is not None:
|
|
1264
|
-
response.headers["Content-Type"] = "application/x-amz-json
|
|
1264
|
+
response.headers["Content-Type"] = f"application/x-amz-json-{json_version}"
|
|
1265
1265
|
response.set_response(
|
|
1266
1266
|
self._serialize_body_params(parameters, shape, operation_model, mime_type, request_id)
|
|
1267
1267
|
)
|
|
@@ -1286,7 +1286,7 @@ class JSONResponseSerializer(ResponseSerializer):
|
|
|
1286
1286
|
def _serialize(self, body: dict, value: Any, shape, key: str | None, mime_type: str):
|
|
1287
1287
|
"""This method dynamically invokes the correct `_serialize_type_*` method for each shape type."""
|
|
1288
1288
|
try:
|
|
1289
|
-
method = getattr(self, "_serialize_type_
|
|
1289
|
+
method = getattr(self, f"_serialize_type_{shape.type_name}", self._default_serialize)
|
|
1290
1290
|
method(body, value, shape, key, mime_type)
|
|
1291
1291
|
except (TypeError, ValueError, AttributeError) as e:
|
|
1292
1292
|
raise ProtocolSerializerError(
|
localstack/aws/scaffold.py
CHANGED
|
@@ -221,7 +221,7 @@ class ShapeNode:
|
|
|
221
221
|
|
|
222
222
|
def _print_as_typed_dict(self, output, doc=True, quote_types=False):
|
|
223
223
|
name = to_valid_python_name(self.shape.name)
|
|
224
|
-
output.write('
|
|
224
|
+
output.write(f'{name} = TypedDict("{name}", {{\n')
|
|
225
225
|
for k, v in self.shape.members.items():
|
|
226
226
|
member_name = to_valid_python_name(v.name)
|
|
227
227
|
# check if the member name is the same as the type name (recursive types need to use forward references)
|
localstack/cli/localstack.py
CHANGED
|
@@ -602,7 +602,7 @@ def cmd_stop() -> None:
|
|
|
602
602
|
|
|
603
603
|
try:
|
|
604
604
|
DOCKER_CLIENT.stop_container(container_name)
|
|
605
|
-
console.print("container stopped:
|
|
605
|
+
console.print(f"container stopped: {container_name}")
|
|
606
606
|
except NoSuchContainer:
|
|
607
607
|
raise CLIError(
|
|
608
608
|
f'Expected a running LocalStack container named "{container_name}", but found none'
|
localstack/cli/plugins.py
CHANGED
localstack/config.py
CHANGED
|
@@ -286,7 +286,7 @@ def ping(host):
|
|
|
286
286
|
"""Returns True if the host responds to a ping request"""
|
|
287
287
|
is_in_windows = is_windows()
|
|
288
288
|
ping_opts = "-n 1 -w 2000" if is_in_windows else "-c 1 -W 2"
|
|
289
|
-
args = "ping
|
|
289
|
+
args = f"ping {ping_opts} {host}"
|
|
290
290
|
return (
|
|
291
291
|
subprocess.call(
|
|
292
292
|
args, shell=not is_in_windows, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
@@ -435,8 +435,8 @@ TMP_FOLDER = os.path.join(tempfile.gettempdir(), "localstack")
|
|
|
435
435
|
VOLUME_DIR = os.environ.get("LOCALSTACK_VOLUME_DIR", "").strip() or TMP_FOLDER
|
|
436
436
|
|
|
437
437
|
# fix for Mac OS, to be able to mount /var/folders in Docker
|
|
438
|
-
if TMP_FOLDER.startswith("/var/folders/") and os.path.exists("/private
|
|
439
|
-
TMP_FOLDER = "/private
|
|
438
|
+
if TMP_FOLDER.startswith("/var/folders/") and os.path.exists(f"/private{TMP_FOLDER}"):
|
|
439
|
+
TMP_FOLDER = f"/private{TMP_FOLDER}"
|
|
440
440
|
|
|
441
441
|
# whether to enable verbose debug logging ("LOG" is used when using the CLI with LOCALSTACK_LOG instead of LS_LOG)
|
|
442
442
|
LS_LOG = eval_log_type("LS_LOG") or eval_log_type("LOG")
|
|
@@ -1237,8 +1237,8 @@ def use_custom_dns():
|
|
|
1237
1237
|
|
|
1238
1238
|
|
|
1239
1239
|
# s3 virtual host name
|
|
1240
|
-
S3_VIRTUAL_HOSTNAME = "s3
|
|
1241
|
-
S3_STATIC_WEBSITE_HOSTNAME = "s3-website
|
|
1240
|
+
S3_VIRTUAL_HOSTNAME = f"s3.{LOCALSTACK_HOST.host}"
|
|
1241
|
+
S3_STATIC_WEBSITE_HOSTNAME = f"s3-website.{LOCALSTACK_HOST.host}"
|
|
1242
1242
|
|
|
1243
1243
|
BOTO_WAITER_DELAY = int(os.environ.get("BOTO_WAITER_DELAY") or "1")
|
|
1244
1244
|
BOTO_WAITER_MAX_ATTEMPTS = int(os.environ.get("BOTO_WAITER_MAX_ATTEMPTS") or "120")
|
|
@@ -363,10 +363,7 @@ def _list_files_in_container_image(container_client: ContainerClient, image_name
|
|
|
363
363
|
try:
|
|
364
364
|
# docker export yields paths without prefixed slashes, so we add them here
|
|
365
365
|
# since the file is pretty big (~4MB for community, ~7MB for pro) we gzip it
|
|
366
|
-
cmd = "docker export
|
|
367
|
-
container_id,
|
|
368
|
-
cache_file,
|
|
369
|
-
)
|
|
366
|
+
cmd = f"docker export {container_id} | tar -t | awk '{{ print \"/\" $0 }}' | gzip > {cache_file}"
|
|
370
367
|
run(cmd, shell=True)
|
|
371
368
|
finally:
|
|
372
369
|
container_client.remove_container(container_id)
|
localstack/runtime/hooks.py
CHANGED
localstack/runtime/init.py
CHANGED
|
@@ -91,7 +91,7 @@ class ShellScriptRunner(ScriptRunner):
|
|
|
91
91
|
def run(self, path: str) -> None:
|
|
92
92
|
exit_code = subprocess.call(args=[], executable=path)
|
|
93
93
|
if exit_code != 0:
|
|
94
|
-
raise OSError("Script
|
|
94
|
+
raise OSError(f"Script {path} returned a non-zero exit code {exit_code}")
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
class PythonScriptRunner(ScriptRunner):
|
localstack/runtime/main.py
CHANGED
|
@@ -20,13 +20,13 @@ def print_runtime_information(in_docker: bool = False):
|
|
|
20
20
|
if in_docker:
|
|
21
21
|
try:
|
|
22
22
|
container_name = get_main_container_name()
|
|
23
|
-
print("LocalStack Docker container name:
|
|
23
|
+
print(f"LocalStack Docker container name: {container_name}")
|
|
24
24
|
inspect_result = DOCKER_CLIENT.inspect_container(container_name)
|
|
25
25
|
container_id = inspect_result["Id"]
|
|
26
|
-
print("LocalStack Docker container id:
|
|
26
|
+
print(f"LocalStack Docker container id: {container_id[:12]}")
|
|
27
27
|
image_details = DOCKER_CLIENT.inspect_image(inspect_result["Image"])
|
|
28
28
|
digests = image_details.get("RepoDigests") or ["Unavailable"]
|
|
29
|
-
print("LocalStack Docker image sha:
|
|
29
|
+
print(f"LocalStack Docker image sha: {digests[0]}")
|
|
30
30
|
except ContainerException:
|
|
31
31
|
print(
|
|
32
32
|
"LocalStack Docker container info: Failed to inspect the LocalStack docker container. "
|
|
@@ -44,10 +44,10 @@ def print_runtime_information(in_docker: bool = False):
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
if config.LOCALSTACK_BUILD_DATE:
|
|
47
|
-
print("LocalStack build date:
|
|
47
|
+
print(f"LocalStack build date: {config.LOCALSTACK_BUILD_DATE}")
|
|
48
48
|
|
|
49
49
|
if config.LOCALSTACK_BUILD_GIT_HASH:
|
|
50
|
-
print("LocalStack build git hash:
|
|
50
|
+
print(f"LocalStack build git hash: {config.LOCALSTACK_BUILD_GIT_HASH}")
|
|
51
51
|
|
|
52
52
|
print()
|
|
53
53
|
|
|
@@ -367,10 +367,7 @@ def resolve_references(data: dict, rest_api_id, allow_recursive=True) -> dict:
|
|
|
367
367
|
|
|
368
368
|
def path_based_url(api_id: str, stage_name: str, path: str) -> str:
|
|
369
369
|
"""Return URL for inbound API gateway for given API ID, stage name, and path"""
|
|
370
|
-
pattern = "
|
|
371
|
-
config.external_service_url(),
|
|
372
|
-
PATH_USER_REQUEST,
|
|
373
|
-
)
|
|
370
|
+
pattern = f"{config.external_service_url()}/restapis/{{api_id}}/{{stage_name}}/{PATH_USER_REQUEST}{{path}}"
|
|
374
371
|
return pattern.format(api_id=api_id, stage_name=stage_name, path=path)
|
|
375
372
|
|
|
376
373
|
|
|
@@ -37,7 +37,7 @@ PATH_REGEX_TEST_INVOKE_API = r"^\/restapis\/([A-Za-z0-9_\-]+)\/resources\/([A-Za
|
|
|
37
37
|
|
|
38
38
|
# regex path pattern for user requests, handles stages like $default
|
|
39
39
|
PATH_REGEX_USER_REQUEST = (
|
|
40
|
-
|
|
40
|
+
rf"^/restapis/([A-Za-z0-9_\\-]+)(?:/([A-Za-z0-9\_($|%24)\\-]+))?/{PATH_USER_REQUEST}/(.*)$"
|
|
41
41
|
)
|
|
42
42
|
# URL pattern for invocations
|
|
43
43
|
HOST_REGEX_EXECUTE_API = r"(?:.*://)?([a-zA-Z0-9]+)(?:(-vpce-[^.]+))?\.execute-api\.(.*)"
|
|
@@ -375,12 +375,12 @@ def get_apigateway_path_for_resource(
|
|
|
375
375
|
path_part = target_resource.get("pathPart", "")
|
|
376
376
|
if path_suffix:
|
|
377
377
|
if path_part:
|
|
378
|
-
path_suffix = "
|
|
378
|
+
path_suffix = f"{path_part}/{path_suffix}"
|
|
379
379
|
else:
|
|
380
380
|
path_suffix = path_part
|
|
381
381
|
parent_id = target_resource.get("parentId")
|
|
382
382
|
if not parent_id:
|
|
383
|
-
return "
|
|
383
|
+
return f"/{path_suffix}"
|
|
384
384
|
return get_apigateway_path_for_resource(
|
|
385
385
|
api_id,
|
|
386
386
|
parent_id,
|
|
@@ -419,7 +419,7 @@ def get_resource_for_path(
|
|
|
419
419
|
for api_path, details in path_map.items():
|
|
420
420
|
api_path_regex = re.sub(r"{[^+]+\+}", r"[^\?#]+", api_path)
|
|
421
421
|
api_path_regex = re.sub(r"{[^}]+}", r"[^/]+", api_path_regex)
|
|
422
|
-
if re.match(
|
|
422
|
+
if re.match(rf"^{api_path_regex}$", path):
|
|
423
423
|
matches.append((api_path, details))
|
|
424
424
|
|
|
425
425
|
# if there are no matches, it's not worth to proceed, bail here!
|
|
@@ -508,8 +508,7 @@ def connect_api_gateway_to_sqs(gateway_name, stage_name, queue_arn, path, accoun
|
|
|
508
508
|
"integrations": [
|
|
509
509
|
{
|
|
510
510
|
"type": "AWS",
|
|
511
|
-
"uri": "arn
|
|
512
|
-
% (partition, sqs_region, sqs_account, queue_name),
|
|
511
|
+
"uri": f"arn:{partition}:apigateway:{sqs_region}:sqs:path/{sqs_account}/{queue_name}",
|
|
513
512
|
"requestTemplates": {"application/json": template},
|
|
514
513
|
"requestParameters": {
|
|
515
514
|
"integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"
|
|
@@ -660,11 +659,11 @@ def set_api_id_stage_invocation_path(
|
|
|
660
659
|
if path_match:
|
|
661
660
|
api_id = path_match.group(1)
|
|
662
661
|
stage = path_match.group(2)
|
|
663
|
-
relative_path_w_query_params = "
|
|
662
|
+
relative_path_w_query_params = f"/{path_match.group(3)}"
|
|
664
663
|
elif host_match:
|
|
665
664
|
api_id = extract_api_id_from_hostname_in_url(host_header)
|
|
666
665
|
stage = path.strip("/").split("/")[0]
|
|
667
|
-
relative_path_w_query_params = "
|
|
666
|
+
relative_path_w_query_params = "/{}".format(path.lstrip("/").partition("/")[2])
|
|
668
667
|
elif test_invoke_match:
|
|
669
668
|
stage = invocation_context.stage
|
|
670
669
|
api_id = invocation_context.api_id
|
|
@@ -783,7 +783,7 @@ class HTTPIntegration(BackendIntegration):
|
|
|
783
783
|
instances = client.list_instances(ServiceId=service_id)["Instances"]
|
|
784
784
|
instance = (instances or [None])[0]
|
|
785
785
|
if instance and instance.get("Id"):
|
|
786
|
-
uri = "http
|
|
786
|
+
uri = "http://{}/{}".format(instance["Id"], invocation_path.lstrip("/"))
|
|
787
787
|
|
|
788
788
|
# apply custom request template
|
|
789
789
|
invocation_context.context = get_event_request_context(invocation_context)
|
|
@@ -977,8 +977,9 @@ class StepFunctionIntegration(BackendIntegration):
|
|
|
977
977
|
headers={"Content-Type": APPLICATION_JSON},
|
|
978
978
|
data=json.dumps(
|
|
979
979
|
{
|
|
980
|
-
"message": "StepFunctions execution
|
|
981
|
-
|
|
980
|
+
"message": "StepFunctions execution {} failed with status '{}'".format(
|
|
981
|
+
result["executionArn"], result_status
|
|
982
|
+
)
|
|
982
983
|
}
|
|
983
984
|
),
|
|
984
985
|
)
|
|
@@ -280,7 +280,7 @@ def invoke_rest_api(invocation_context: ApiInvocationContext):
|
|
|
280
280
|
|
|
281
281
|
extracted_path, resource = get_target_resource_details(invocation_context)
|
|
282
282
|
if not resource:
|
|
283
|
-
return make_error_response("Unable to find path
|
|
283
|
+
return make_error_response(f"Unable to find path {invocation_context.path}", 404)
|
|
284
284
|
|
|
285
285
|
# validate request
|
|
286
286
|
validator = RequestValidator(invocation_context)
|
|
@@ -307,7 +307,7 @@ def invoke_rest_api(invocation_context: ApiInvocationContext):
|
|
|
307
307
|
# default to returning CORS headers if this is an OPTIONS request
|
|
308
308
|
return get_cors_response(headers)
|
|
309
309
|
return make_error_response(
|
|
310
|
-
"Unable to find integration for:
|
|
310
|
+
f"Unable to find integration for: {method} {invocation_path} ({raw_path})",
|
|
311
311
|
404,
|
|
312
312
|
)
|
|
313
313
|
|
|
@@ -3025,7 +3025,7 @@ def to_documentation_part_response_json(api_id, data):
|
|
|
3025
3025
|
|
|
3026
3026
|
|
|
3027
3027
|
def to_base_mapping_response_json(domain_name, base_path, data):
|
|
3028
|
-
self_link = "/domainnames
|
|
3028
|
+
self_link = f"/domainnames/{domain_name}/basepathmappings/{base_path}"
|
|
3029
3029
|
result = to_response_json("basepathmapping", data, self_link=self_link)
|
|
3030
3030
|
result = select_from_typed_dict(BasePathMapping, result)
|
|
3031
3031
|
return result
|
|
@@ -3061,9 +3061,9 @@ def to_response_json(model_type, data, api_id=None, self_link=None, id_attr=None
|
|
|
3061
3061
|
id_attr = id_attr or "id"
|
|
3062
3062
|
result = deepcopy(data)
|
|
3063
3063
|
if not self_link:
|
|
3064
|
-
self_link = "
|
|
3064
|
+
self_link = f"/{model_type}s/{data[id_attr]}"
|
|
3065
3065
|
if api_id:
|
|
3066
|
-
self_link = "/restapis
|
|
3066
|
+
self_link = f"/restapis/{api_id}/{self_link}"
|
|
3067
3067
|
# TODO: check if this is still required - "_links" are listed in the sample responses in the docs, but
|
|
3068
3068
|
# recent parity tests indicate that this field is not returned by real AWS...
|
|
3069
3069
|
# https://docs.aws.amazon.com/apigateway/latest/api/API_GetAuthorizers.html#API_GetAuthorizers_Example_1_Response
|
|
@@ -3075,7 +3075,7 @@ def to_response_json(model_type, data, api_id=None, self_link=None, id_attr=None
|
|
|
3075
3075
|
"name": model_type,
|
|
3076
3076
|
"templated": True,
|
|
3077
3077
|
}
|
|
3078
|
-
result["_links"]["
|
|
3078
|
+
result["_links"][f"{model_type}:delete"] = {"href": self_link}
|
|
3079
3079
|
return result
|
|
3080
3080
|
|
|
3081
3081
|
|
|
@@ -72,7 +72,7 @@ class ApiGatewayResourceProvider(ResourceProvider[ApiGatewayResourceProperties])
|
|
|
72
72
|
root_resource = ([r for r in resources if r["path"] == "/"] or [None])[0]
|
|
73
73
|
if not root_resource:
|
|
74
74
|
raise Exception(
|
|
75
|
-
"Unable to find root resource for REST API
|
|
75
|
+
"Unable to find root resource for REST API {}".format(params["restApiId"])
|
|
76
76
|
)
|
|
77
77
|
params["parentId"] = root_resource["id"]
|
|
78
78
|
response = apigw.create_resource(**params)
|
|
@@ -77,9 +77,7 @@ def get_remote_template_body(url: str) -> str:
|
|
|
77
77
|
result = client.get_object(Bucket=parts[0], Key=parts[2])
|
|
78
78
|
body = to_str(result["Body"].read())
|
|
79
79
|
return body
|
|
80
|
-
raise RuntimeError(
|
|
81
|
-
"Unable to fetch template body (code %s) from URL %s" % (status_code, url)
|
|
82
|
-
)
|
|
80
|
+
raise RuntimeError(f"Unable to fetch template body (code {status_code}) from URL {url}")
|
|
83
81
|
else:
|
|
84
82
|
raise RuntimeError(
|
|
85
83
|
f"Bad status code from fetching template from url '{url}' ({status_code})",
|
|
@@ -112,11 +110,9 @@ def get_template_body(req_data: dict) -> str:
|
|
|
112
110
|
result = client.get_object(Bucket=parts[0], Key=parts[2])
|
|
113
111
|
body = to_str(result["Body"].read())
|
|
114
112
|
return body
|
|
115
|
-
raise Exception(
|
|
116
|
-
"Unable to fetch template body (code %s) from URL %s" % (status_code, url)
|
|
117
|
-
)
|
|
113
|
+
raise Exception(f"Unable to fetch template body (code {status_code}) from URL {url}")
|
|
118
114
|
return to_str(response.content)
|
|
119
|
-
raise Exception("Unable to get template body from input:
|
|
115
|
+
raise Exception(f"Unable to get template body from input: {req_data}")
|
|
120
116
|
|
|
121
117
|
|
|
122
118
|
def is_local_service_url(url: str) -> bool:
|
|
@@ -127,7 +123,7 @@ def is_local_service_url(url: str) -> bool:
|
|
|
127
123
|
constants.LOCALHOST_HOSTNAME,
|
|
128
124
|
localstack_host().host,
|
|
129
125
|
)
|
|
130
|
-
if any(re.match(
|
|
126
|
+
if any(re.match(rf"^[^:]+://[^:/]*{host}([:/]|$)", url) for host in candidates):
|
|
131
127
|
return True
|
|
132
128
|
host = url.split("://")[-1].split("/")[0]
|
|
133
129
|
return "localhost" in host
|
|
@@ -59,7 +59,7 @@ def convert_types(obj, types):
|
|
|
59
59
|
def recurse(o, path):
|
|
60
60
|
if isinstance(o, dict):
|
|
61
61
|
for k, v in dict(o).items():
|
|
62
|
-
key_path = "
|
|
62
|
+
key_path = "{}{}".format(path or ".", k)
|
|
63
63
|
if key in [k, key_path]:
|
|
64
64
|
o[k] = type_class(v)
|
|
65
65
|
return o
|
|
@@ -360,8 +360,7 @@ class Stack:
|
|
|
360
360
|
resource = resource_map.get(resource_id)
|
|
361
361
|
if not resource:
|
|
362
362
|
raise Exception(
|
|
363
|
-
'Unable to find details for resource "
|
|
364
|
-
% (resource_id, self.stack_name)
|
|
363
|
+
f'Unable to find details for resource "{resource_id}" in stack "{self.stack_name}"'
|
|
365
364
|
)
|
|
366
365
|
return resource
|
|
367
366
|
|
|
@@ -329,7 +329,7 @@ def _resolve_refs_recursively(
|
|
|
329
329
|
account_id, region_name, stack_name, resources, parameters, value["Ref"]
|
|
330
330
|
)
|
|
331
331
|
if ref is None:
|
|
332
|
-
msg = 'Unable to resolve Ref for resource "
|
|
332
|
+
msg = 'Unable to resolve Ref for resource "{}" (yet)'.format(value["Ref"])
|
|
333
333
|
LOG.debug("%s - %s", msg, resources.get(value["Ref"]) or set(resources.keys()))
|
|
334
334
|
|
|
335
335
|
raise DependencyNotYetSatisfied(resource_ids=value["Ref"], message=msg)
|
|
@@ -450,7 +450,7 @@ def _resolve_refs_recursively(
|
|
|
450
450
|
raise DependencyNotYetSatisfied(
|
|
451
451
|
resource_ids=key, message=f"Could not resolve {val} to terminal value type"
|
|
452
452
|
)
|
|
453
|
-
result = result.replace("${
|
|
453
|
+
result = result.replace(f"${{{key}}}", str(resolved_val))
|
|
454
454
|
|
|
455
455
|
# resolve placeholders
|
|
456
456
|
result = resolve_placeholders_in_string(
|
|
@@ -146,7 +146,7 @@ class ChangeSetModelTransform(ChangeSetModelPreproc):
|
|
|
146
146
|
if not location or not location.startswith("s3://"):
|
|
147
147
|
raise FailedTransformationException(
|
|
148
148
|
transformation=INCLUDE_TRANSFORM,
|
|
149
|
-
message="Unexpected Location parameter for AWS::Include transformer:
|
|
149
|
+
message=f"Unexpected Location parameter for AWS::Include transformer: {location}",
|
|
150
150
|
)
|
|
151
151
|
|
|
152
152
|
s3_client = connect_to(
|
|
@@ -158,7 +158,7 @@ class ChangeSetModelTransform(ChangeSetModelPreproc):
|
|
|
158
158
|
except ClientError:
|
|
159
159
|
raise FailedTransformationException(
|
|
160
160
|
transformation=INCLUDE_TRANSFORM,
|
|
161
|
-
message="Error downloading S3 object '
|
|
161
|
+
message=f"Error downloading S3 object '{bucket}/{path}'",
|
|
162
162
|
)
|
|
163
163
|
try:
|
|
164
164
|
template_to_include = parse_template(content)
|
|
@@ -159,7 +159,7 @@ def find_stack_instance(stack_set: StackSet, account: str, region: str):
|
|
|
159
159
|
|
|
160
160
|
def stack_not_found_error(stack_name: str):
|
|
161
161
|
# FIXME
|
|
162
|
-
raise ValidationError("Stack with id
|
|
162
|
+
raise ValidationError(f"Stack with id {stack_name} does not exist")
|
|
163
163
|
|
|
164
164
|
|
|
165
165
|
def not_found_error(message: str):
|
|
@@ -305,7 +305,7 @@ class CloudformationProvider(CloudformationApi):
|
|
|
305
305
|
deployer.deploy_stack()
|
|
306
306
|
except Exception as e:
|
|
307
307
|
stack.set_stack_status("CREATE_FAILED")
|
|
308
|
-
msg = 'Unable to create stack "
|
|
308
|
+
msg = f'Unable to create stack "{stack.stack_name}": {e}'
|
|
309
309
|
LOG.error("%s", exc_info=LOG.isEnabledFor(logging.DEBUG))
|
|
310
310
|
raise ValidationError(msg) from e
|
|
311
311
|
|
|
@@ -13,7 +13,7 @@ class DependencyNotYetSatisfied(Exception):
|
|
|
13
13
|
"""Exception indicating that a resource dependency is not (yet) deployed/available."""
|
|
14
14
|
|
|
15
15
|
def __init__(self, resource_ids, message=None):
|
|
16
|
-
message = message or "Unresolved dependencies:
|
|
16
|
+
message = message or f"Unresolved dependencies: {resource_ids}"
|
|
17
17
|
super().__init__(message)
|
|
18
18
|
resource_ids = resource_ids if isinstance(resource_ids, list) else [resource_ids]
|
|
19
19
|
self.resource_ids = resource_ids
|
|
@@ -162,7 +162,7 @@ class DynamodbServer(Server):
|
|
|
162
162
|
cmd = [
|
|
163
163
|
"java",
|
|
164
164
|
*self._get_java_vm_options(),
|
|
165
|
-
"-Xmx
|
|
165
|
+
f"-Xmx{self.heap_size}",
|
|
166
166
|
f"-javaagent:{dynamodblocal_package.get_installer().get_ddb_agent_jar_path()}",
|
|
167
167
|
f"-Djava.library.path={self.library_path}",
|
|
168
168
|
"-jar",
|
localstack/services/edge.py
CHANGED
|
@@ -72,7 +72,7 @@ def start_component(
|
|
|
72
72
|
default_port=constants.DEFAULT_PORT_EDGE,
|
|
73
73
|
),
|
|
74
74
|
)
|
|
75
|
-
raise Exception("Unexpected component name '
|
|
75
|
+
raise Exception(f"Unexpected component name '{component}' received during start up")
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
def start_proxy(
|
|
@@ -206,12 +206,12 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
206
206
|
raise ValueError("If the value is greater than 1, the unit must be plural")
|
|
207
207
|
|
|
208
208
|
if "minute" in unit:
|
|
209
|
-
return "
|
|
209
|
+
return f"*/{value} * * * *"
|
|
210
210
|
if "hour" in unit:
|
|
211
|
-
return "0
|
|
211
|
+
return f"0 */{value} * * *"
|
|
212
212
|
if "day" in unit:
|
|
213
|
-
return "0 0
|
|
214
|
-
raise ValueError("Unable to parse events schedule expression:
|
|
213
|
+
return f"0 0 */{value} * *"
|
|
214
|
+
raise ValueError(f"Unable to parse events schedule expression: {schedule}")
|
|
215
215
|
return schedule
|
|
216
216
|
|
|
217
217
|
@staticmethod
|
|
@@ -374,7 +374,7 @@ def _dump_events_to_files(events_with_added_uuid):
|
|
|
374
374
|
for event in events_with_added_uuid:
|
|
375
375
|
target = os.path.join(
|
|
376
376
|
_get_events_tmp_dir(),
|
|
377
|
-
"
|
|
377
|
+
"{}_{}".format(current_time_millis, event["uuid"]),
|
|
378
378
|
)
|
|
379
379
|
save_file(target, json.dumps(event["event"]))
|
|
380
380
|
except Exception as e:
|
|
@@ -49,7 +49,7 @@ def find_stream_for_consumer(consumer_arn):
|
|
|
49
49
|
for cons in kinesis.list_stream_consumers(StreamARN=stream_arn)["Consumers"]:
|
|
50
50
|
if cons["ConsumerARN"] == consumer_arn:
|
|
51
51
|
return stream_name
|
|
52
|
-
raise Exception("Unable to find stream for stream consumer
|
|
52
|
+
raise Exception(f"Unable to find stream for stream consumer {consumer_arn}")
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
class KinesisProvider(KinesisApi, ServiceLifecycleHook):
|
localstack/services/plugins.py
CHANGED
|
@@ -286,19 +286,19 @@ class ServiceManager:
|
|
|
286
286
|
container = self.get_service_container(name)
|
|
287
287
|
|
|
288
288
|
if not container:
|
|
289
|
-
raise ValueError("no such service
|
|
289
|
+
raise ValueError(f"no such service {name}")
|
|
290
290
|
|
|
291
291
|
if container.state == ServiceState.STARTING:
|
|
292
292
|
if not poll_condition(lambda: container.state != ServiceState.STARTING, timeout=30):
|
|
293
|
-
raise TimeoutError("gave up waiting for service
|
|
293
|
+
raise TimeoutError(f"gave up waiting for service {name} to start")
|
|
294
294
|
|
|
295
295
|
if container.state == ServiceState.STOPPING:
|
|
296
296
|
if not poll_condition(lambda: container.state == ServiceState.STOPPED, timeout=30):
|
|
297
|
-
raise TimeoutError("gave up waiting for service
|
|
297
|
+
raise TimeoutError(f"gave up waiting for service {name} to stop")
|
|
298
298
|
|
|
299
299
|
with container.lock:
|
|
300
300
|
if container.state == ServiceState.DISABLED:
|
|
301
|
-
raise ServiceDisabled("service
|
|
301
|
+
raise ServiceDisabled(f"service {name} is disabled")
|
|
302
302
|
|
|
303
303
|
if container.state == ServiceState.RUNNING:
|
|
304
304
|
return container.service
|
|
@@ -314,7 +314,7 @@ class ServiceManager:
|
|
|
314
314
|
raise container.errors[-1]
|
|
315
315
|
|
|
316
316
|
raise ServiceStateException(
|
|
317
|
-
"service
|
|
317
|
+
f"service {name} is not ready ({container.state}) and could not be started"
|
|
318
318
|
)
|
|
319
319
|
|
|
320
320
|
# legacy map compatibility
|
|
@@ -692,7 +692,7 @@ def check_service_health(api, expect_shutdown=False):
|
|
|
692
692
|
LOG.warning('Service "%s" not yet available, retrying...', api)
|
|
693
693
|
else:
|
|
694
694
|
LOG.warning('Service "%s" still shutting down, retrying...', api)
|
|
695
|
-
raise Exception("Service check failed for api:
|
|
695
|
+
raise Exception(f"Service check failed for api: {api}")
|
|
696
696
|
|
|
697
697
|
|
|
698
698
|
@hooks.on_infra_start(should_load=lambda: config.EAGER_SERVICE_LOADING)
|
|
@@ -18,7 +18,7 @@ DEDUPLICATION_INTERVAL_IN_SEC = 5 * 60
|
|
|
18
18
|
RECENTLY_DELETED_TIMEOUT = 60
|
|
19
19
|
|
|
20
20
|
# the default maximum message size in SQS
|
|
21
|
-
DEFAULT_MAXIMUM_MESSAGE_SIZE =
|
|
21
|
+
DEFAULT_MAXIMUM_MESSAGE_SIZE = 1048576
|
|
22
22
|
INTERNAL_QUEUE_ATTRIBUTES = [
|
|
23
23
|
# these attributes cannot be changed by set_queue_attributes and should
|
|
24
24
|
# therefore be ignored when comparing queue attributes for create_queue
|