localstack-core 4.9.3.dev25__py3-none-any.whl → 4.9.3.dev27__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/handlers/metric_handler.py +41 -1
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +4 -4
- localstack/cli/lpm.py +3 -4
- localstack/cli/profiles.py +1 -2
- localstack/config.py +18 -12
- localstack/constants.py +4 -0
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +20 -19
- localstack/testing/pytest/marking.py +5 -4
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +1 -2
- localstack/utils/analytics/metrics/counter.py +6 -8
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/aws/arns.py +7 -7
- localstack/utils/aws/aws_responses.py +7 -7
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +7 -7
- 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 +115 -131
- localstack/utils/container_utils/docker_cmd_client.py +42 -42
- localstack/utils/container_utils/docker_sdk_client.py +63 -62
- localstack/utils/diagnose.py +2 -3
- localstack/utils/docker_utils.py +3 -4
- localstack/utils/functions.py +3 -2
- localstack/utils/http.py +4 -5
- localstack/utils/json.py +3 -3
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +6 -6
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +2 -2
- localstack/utils/objects.py +6 -5
- localstack/utils/patch.py +2 -1
- localstack/utils/run.py +10 -9
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +2 -3
- localstack/utils/strings.py +10 -11
- localstack/utils/sync.py +2 -1
- localstack/utils/tagging.py +1 -4
- localstack/utils/testutil.py +5 -4
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +1 -2
- localstack/utils/urls.py +1 -3
- localstack/version.py +2 -2
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/METADATA +2 -2
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/RECORD +69 -69
- localstack_core-4.9.3.dev27.dist-info/plux.json +1 -0
- localstack_core-4.9.3.dev25.dist-info/plux.json +0 -1
- {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack +0 -0
- {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/WHEEL +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ import os
|
|
|
6
6
|
import re
|
|
7
7
|
import shlex
|
|
8
8
|
import subprocess
|
|
9
|
-
from
|
|
9
|
+
from collections.abc import Callable
|
|
10
10
|
|
|
11
11
|
from localstack import config
|
|
12
12
|
from localstack.utils.collections import ensure_list
|
|
@@ -96,7 +96,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
96
96
|
different response payloads or error messages returned by the `docker` vs `podman` commands.
|
|
97
97
|
"""
|
|
98
98
|
|
|
99
|
-
default_run_outfile:
|
|
99
|
+
default_run_outfile: str | None = None
|
|
100
100
|
|
|
101
101
|
def _docker_cmd(self) -> list[str]:
|
|
102
102
|
"""
|
|
@@ -289,7 +289,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
289
289
|
f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
|
|
290
290
|
) from e
|
|
291
291
|
|
|
292
|
-
def list_containers(self, filter:
|
|
292
|
+
def list_containers(self, filter: list[str] | str | None = None, all=True) -> list[dict]:
|
|
293
293
|
filter = [filter] if isinstance(filter, str) else filter
|
|
294
294
|
cmd = self._docker_cmd()
|
|
295
295
|
cmd.append("ps")
|
|
@@ -365,8 +365,8 @@ class CmdDockerClient(ContainerClient):
|
|
|
365
365
|
def pull_image(
|
|
366
366
|
self,
|
|
367
367
|
docker_image: str,
|
|
368
|
-
platform:
|
|
369
|
-
log_handler:
|
|
368
|
+
platform: DockerPlatform | None = None,
|
|
369
|
+
log_handler: Callable[[str], None] | None = None,
|
|
370
370
|
) -> None:
|
|
371
371
|
cmd = self._docker_cmd()
|
|
372
372
|
docker_image = self.registry_resolver_strategy.resolve(docker_image)
|
|
@@ -420,7 +420,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
420
420
|
dockerfile_path: str,
|
|
421
421
|
image_name: str,
|
|
422
422
|
context_path: str = None,
|
|
423
|
-
platform:
|
|
423
|
+
platform: DockerPlatform | None = None,
|
|
424
424
|
):
|
|
425
425
|
cmd = self._docker_cmd()
|
|
426
426
|
dockerfile_path = Util.resolve_dockerfile_path(dockerfile_path)
|
|
@@ -497,7 +497,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
497
497
|
|
|
498
498
|
return CancellableProcessStream(process)
|
|
499
499
|
|
|
500
|
-
def _inspect_object(self, object_name_or_id: str) -> dict[str,
|
|
500
|
+
def _inspect_object(self, object_name_or_id: str) -> dict[str, dict | list | str]:
|
|
501
501
|
cmd = self._docker_cmd()
|
|
502
502
|
cmd += ["inspect", "--format", "{{json .}}", object_name_or_id]
|
|
503
503
|
try:
|
|
@@ -524,7 +524,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
524
524
|
)
|
|
525
525
|
return object_data
|
|
526
526
|
|
|
527
|
-
def inspect_container(self, container_name_or_id: str) -> dict[str,
|
|
527
|
+
def inspect_container(self, container_name_or_id: str) -> dict[str, dict | str]:
|
|
528
528
|
try:
|
|
529
529
|
return self._inspect_object(container_name_or_id)
|
|
530
530
|
except NoSuchObject as e:
|
|
@@ -535,7 +535,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
535
535
|
image_name: str,
|
|
536
536
|
pull: bool = True,
|
|
537
537
|
strip_wellknown_repo_prefixes: bool = True,
|
|
538
|
-
) -> dict[str,
|
|
538
|
+
) -> dict[str, dict | list | str]:
|
|
539
539
|
image_name = self.registry_resolver_strategy.resolve(image_name)
|
|
540
540
|
try:
|
|
541
541
|
result = self._inspect_object(image_name)
|
|
@@ -577,7 +577,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
577
577
|
f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
|
|
578
578
|
) from e
|
|
579
579
|
|
|
580
|
-
def inspect_network(self, network_name: str) -> dict[str,
|
|
580
|
+
def inspect_network(self, network_name: str) -> dict[str, dict | str]:
|
|
581
581
|
try:
|
|
582
582
|
return self._inspect_object(network_name)
|
|
583
583
|
except NoSuchObject as e:
|
|
@@ -587,7 +587,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
587
587
|
self,
|
|
588
588
|
network_name: str,
|
|
589
589
|
container_name_or_id: str,
|
|
590
|
-
aliases:
|
|
590
|
+
aliases: list | None = None,
|
|
591
591
|
link_local_ips: list[str] = None,
|
|
592
592
|
) -> None:
|
|
593
593
|
LOG.debug(
|
|
@@ -652,7 +652,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
652
652
|
f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
|
|
653
653
|
) from e
|
|
654
654
|
|
|
655
|
-
def login(self, username: str, password: str, registry:
|
|
655
|
+
def login(self, username: str, password: str, registry: str | None = None) -> None:
|
|
656
656
|
cmd = self._docker_cmd()
|
|
657
657
|
# TODO specify password via stdin
|
|
658
658
|
cmd += ["login", "-u", username, "-p", password]
|
|
@@ -709,13 +709,13 @@ class CmdDockerClient(ContainerClient):
|
|
|
709
709
|
def exec_in_container(
|
|
710
710
|
self,
|
|
711
711
|
container_name_or_id: str,
|
|
712
|
-
command:
|
|
712
|
+
command: list[str] | str,
|
|
713
713
|
interactive=False,
|
|
714
714
|
detach=False,
|
|
715
|
-
env_vars:
|
|
716
|
-
stdin:
|
|
717
|
-
user:
|
|
718
|
-
workdir:
|
|
715
|
+
env_vars: dict[str, str | None] | None = None,
|
|
716
|
+
stdin: bytes | None = None,
|
|
717
|
+
user: str | None = None,
|
|
718
|
+
workdir: str | None = None,
|
|
719
719
|
) -> tuple[bytes, bytes]:
|
|
720
720
|
env_file = None
|
|
721
721
|
cmd = self._docker_cmd()
|
|
@@ -745,7 +745,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
745
745
|
stdin=None,
|
|
746
746
|
interactive: bool = False,
|
|
747
747
|
attach: bool = False,
|
|
748
|
-
flags:
|
|
748
|
+
flags: str | None = None,
|
|
749
749
|
) -> tuple[bytes, bytes]:
|
|
750
750
|
cmd = self._docker_cmd() + ["start"]
|
|
751
751
|
if flags:
|
|
@@ -803,31 +803,31 @@ class CmdDockerClient(ContainerClient):
|
|
|
803
803
|
action: str,
|
|
804
804
|
image_name: str,
|
|
805
805
|
*,
|
|
806
|
-
name:
|
|
807
|
-
entrypoint:
|
|
806
|
+
name: str | None = None,
|
|
807
|
+
entrypoint: list[str] | str | None = None,
|
|
808
808
|
remove: bool = False,
|
|
809
809
|
interactive: bool = False,
|
|
810
810
|
tty: bool = False,
|
|
811
811
|
detach: bool = False,
|
|
812
|
-
command:
|
|
813
|
-
volumes:
|
|
814
|
-
ports:
|
|
815
|
-
exposed_ports:
|
|
816
|
-
env_vars:
|
|
817
|
-
user:
|
|
818
|
-
cap_add:
|
|
819
|
-
cap_drop:
|
|
820
|
-
security_opt:
|
|
821
|
-
network:
|
|
822
|
-
dns:
|
|
823
|
-
additional_flags:
|
|
824
|
-
workdir:
|
|
825
|
-
privileged:
|
|
826
|
-
labels:
|
|
827
|
-
platform:
|
|
828
|
-
ulimits:
|
|
829
|
-
init:
|
|
830
|
-
log_config:
|
|
812
|
+
command: list[str] | str | None = None,
|
|
813
|
+
volumes: list[SimpleVolumeBind] | None = None,
|
|
814
|
+
ports: PortMappings | None = None,
|
|
815
|
+
exposed_ports: list[str] | None = None,
|
|
816
|
+
env_vars: dict[str, str] | None = None,
|
|
817
|
+
user: str | None = None,
|
|
818
|
+
cap_add: list[str] | None = None,
|
|
819
|
+
cap_drop: list[str] | None = None,
|
|
820
|
+
security_opt: list[str] | None = None,
|
|
821
|
+
network: str | None = None,
|
|
822
|
+
dns: str | list[str] | None = None,
|
|
823
|
+
additional_flags: str | None = None,
|
|
824
|
+
workdir: str | None = None,
|
|
825
|
+
privileged: bool | None = None,
|
|
826
|
+
labels: dict[str, str] | None = None,
|
|
827
|
+
platform: DockerPlatform | None = None,
|
|
828
|
+
ulimits: list[Ulimit] | None = None,
|
|
829
|
+
init: bool | None = None,
|
|
830
|
+
log_config: LogConfig | None = None,
|
|
831
831
|
) -> tuple[list[str], str]:
|
|
832
832
|
env_file = None
|
|
833
833
|
cmd = self._docker_cmd() + [action]
|
|
@@ -900,7 +900,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
900
900
|
return cmd, env_file
|
|
901
901
|
|
|
902
902
|
@staticmethod
|
|
903
|
-
def _map_to_volume_param(volume:
|
|
903
|
+
def _map_to_volume_param(volume: SimpleVolumeBind | BindMount | VolumeDirMount) -> str:
|
|
904
904
|
"""
|
|
905
905
|
Maps the mount volume, to a parameter for the -v docker cli argument.
|
|
906
906
|
|
|
@@ -928,7 +928,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
928
928
|
)
|
|
929
929
|
|
|
930
930
|
def _check_output_and_raise_no_such_container_error(
|
|
931
|
-
self, container_name_or_id: str, output: str, error:
|
|
931
|
+
self, container_name_or_id: str, output: str, error: str | None = None
|
|
932
932
|
):
|
|
933
933
|
"""
|
|
934
934
|
Check the given client invocation output and raise a `NoSuchContainer` exception if it
|
|
@@ -938,7 +938,7 @@ class CmdDockerClient(ContainerClient):
|
|
|
938
938
|
if any(msg.lower() in output.lower() for msg in possible_not_found_messages):
|
|
939
939
|
raise NoSuchContainer(container_name_or_id, stdout=output, stderr=error)
|
|
940
940
|
|
|
941
|
-
def _transform_container_labels(self, labels:
|
|
941
|
+
def _transform_container_labels(self, labels: str | dict[str, str]) -> dict[str, str]:
|
|
942
942
|
"""
|
|
943
943
|
Transforms the container labels returned by the docker command from the key-value pair format to a dict
|
|
944
944
|
:param labels: Input string, comma separated key value pairs. Example: key1=value1,key2=value2
|
|
@@ -6,9 +6,10 @@ import queue
|
|
|
6
6
|
import re
|
|
7
7
|
import socket
|
|
8
8
|
import threading
|
|
9
|
+
from collections.abc import Callable
|
|
9
10
|
from functools import cache
|
|
10
11
|
from time import sleep
|
|
11
|
-
from typing import
|
|
12
|
+
from typing import cast
|
|
12
13
|
from urllib.parse import quote
|
|
13
14
|
|
|
14
15
|
import docker
|
|
@@ -56,7 +57,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
56
57
|
is doing some of the heavy lifting for us to support both target platforms.
|
|
57
58
|
"""
|
|
58
59
|
|
|
59
|
-
docker_client:
|
|
60
|
+
docker_client: DockerClient | None
|
|
60
61
|
|
|
61
62
|
def __init__(self):
|
|
62
63
|
try:
|
|
@@ -280,7 +281,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
280
281
|
except APIError as e:
|
|
281
282
|
raise ContainerException() from e
|
|
282
283
|
|
|
283
|
-
def list_containers(self, filter:
|
|
284
|
+
def list_containers(self, filter: list[str] | str | None = None, all=True) -> list[dict]:
|
|
284
285
|
if filter:
|
|
285
286
|
filter = [filter] if isinstance(filter, str) else filter
|
|
286
287
|
filter = dict([f.split("=", 1) for f in filter])
|
|
@@ -339,14 +340,14 @@ class SdkDockerClient(ContainerClient):
|
|
|
339
340
|
def pull_image(
|
|
340
341
|
self,
|
|
341
342
|
docker_image: str,
|
|
342
|
-
platform:
|
|
343
|
-
log_handler:
|
|
343
|
+
platform: DockerPlatform | None = None,
|
|
344
|
+
log_handler: Callable[[str], None] | None = None,
|
|
344
345
|
) -> None:
|
|
345
346
|
LOG.debug("Pulling Docker image: %s", docker_image)
|
|
346
347
|
# some path in the docker image string indicates a custom repository
|
|
347
348
|
|
|
348
349
|
docker_image = self.registry_resolver_strategy.resolve(docker_image)
|
|
349
|
-
kwargs: dict[str,
|
|
350
|
+
kwargs: dict[str, str | bool] = {"platform": platform}
|
|
350
351
|
try:
|
|
351
352
|
if log_handler:
|
|
352
353
|
# Use a lower-level API, as the 'stream' argument is not available in the higher-level `pull`-API
|
|
@@ -391,7 +392,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
391
392
|
dockerfile_path: str,
|
|
392
393
|
image_name: str,
|
|
393
394
|
context_path: str = None,
|
|
394
|
-
platform:
|
|
395
|
+
platform: DockerPlatform | None = None,
|
|
395
396
|
):
|
|
396
397
|
try:
|
|
397
398
|
dockerfile_path = Util.resolve_dockerfile_path(dockerfile_path)
|
|
@@ -468,7 +469,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
468
469
|
except APIError as e:
|
|
469
470
|
raise ContainerException() from e
|
|
470
471
|
|
|
471
|
-
def inspect_container(self, container_name_or_id: str) -> dict[str,
|
|
472
|
+
def inspect_container(self, container_name_or_id: str) -> dict[str, dict | str]:
|
|
472
473
|
try:
|
|
473
474
|
return self.client().containers.get(container_name_or_id).attrs
|
|
474
475
|
except NotFound:
|
|
@@ -481,7 +482,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
481
482
|
image_name: str,
|
|
482
483
|
pull: bool = True,
|
|
483
484
|
strip_wellknown_repo_prefixes: bool = True,
|
|
484
|
-
) -> dict[str,
|
|
485
|
+
) -> dict[str, dict | list | str]:
|
|
485
486
|
image_name = self.registry_resolver_strategy.resolve(image_name)
|
|
486
487
|
try:
|
|
487
488
|
result = self.client().images.get(image_name).attrs
|
|
@@ -515,7 +516,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
515
516
|
except APIError as e:
|
|
516
517
|
raise ContainerException() from e
|
|
517
518
|
|
|
518
|
-
def inspect_network(self, network_name: str) -> dict[str,
|
|
519
|
+
def inspect_network(self, network_name: str) -> dict[str, dict | str]:
|
|
519
520
|
try:
|
|
520
521
|
return self.client().networks.get(network_name).attrs
|
|
521
522
|
except NotFound:
|
|
@@ -527,7 +528,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
527
528
|
self,
|
|
528
529
|
network_name: str,
|
|
529
530
|
container_name_or_id: str,
|
|
530
|
-
aliases:
|
|
531
|
+
aliases: list | None = None,
|
|
531
532
|
link_local_ips: list[str] = None,
|
|
532
533
|
) -> None:
|
|
533
534
|
LOG.debug(
|
|
@@ -621,7 +622,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
621
622
|
stdin=None,
|
|
622
623
|
interactive: bool = False,
|
|
623
624
|
attach: bool = False,
|
|
624
|
-
flags:
|
|
625
|
+
flags: str | None = None,
|
|
625
626
|
) -> tuple[bytes, bytes]:
|
|
626
627
|
LOG.debug("Starting container %s", container_name_or_id)
|
|
627
628
|
try:
|
|
@@ -670,7 +671,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
670
671
|
sock.sendall(to_bytes(stdin))
|
|
671
672
|
sock.shutdown(socket.SHUT_WR)
|
|
672
673
|
stdout, stderr = self._read_from_sock(sock, False)
|
|
673
|
-
except
|
|
674
|
+
except TimeoutError:
|
|
674
675
|
LOG.debug(
|
|
675
676
|
"Socket timeout when talking to the I/O streams of Docker container '%s'",
|
|
676
677
|
container_name_or_id,
|
|
@@ -703,31 +704,31 @@ class SdkDockerClient(ContainerClient):
|
|
|
703
704
|
self,
|
|
704
705
|
image_name: str,
|
|
705
706
|
*,
|
|
706
|
-
name:
|
|
707
|
-
entrypoint:
|
|
707
|
+
name: str | None = None,
|
|
708
|
+
entrypoint: list[str] | str | None = None,
|
|
708
709
|
remove: bool = False,
|
|
709
710
|
interactive: bool = False,
|
|
710
711
|
tty: bool = False,
|
|
711
712
|
detach: bool = False,
|
|
712
|
-
command:
|
|
713
|
-
volumes:
|
|
714
|
-
ports:
|
|
715
|
-
exposed_ports:
|
|
716
|
-
env_vars:
|
|
717
|
-
user:
|
|
718
|
-
cap_add:
|
|
719
|
-
cap_drop:
|
|
720
|
-
security_opt:
|
|
721
|
-
network:
|
|
722
|
-
dns:
|
|
723
|
-
additional_flags:
|
|
724
|
-
workdir:
|
|
725
|
-
privileged:
|
|
726
|
-
labels:
|
|
727
|
-
platform:
|
|
728
|
-
ulimits:
|
|
729
|
-
init:
|
|
730
|
-
log_config:
|
|
713
|
+
command: list[str] | str | None = None,
|
|
714
|
+
volumes: list[SimpleVolumeBind] | None = None,
|
|
715
|
+
ports: PortMappings | None = None,
|
|
716
|
+
exposed_ports: list[str] | None = None,
|
|
717
|
+
env_vars: dict[str, str] | None = None,
|
|
718
|
+
user: str | None = None,
|
|
719
|
+
cap_add: list[str] | None = None,
|
|
720
|
+
cap_drop: list[str] | None = None,
|
|
721
|
+
security_opt: list[str] | None = None,
|
|
722
|
+
network: str | None = None,
|
|
723
|
+
dns: str | list[str] | None = None,
|
|
724
|
+
additional_flags: str | None = None,
|
|
725
|
+
workdir: str | None = None,
|
|
726
|
+
privileged: bool | None = None,
|
|
727
|
+
labels: dict[str, str] | None = None,
|
|
728
|
+
platform: DockerPlatform | None = None,
|
|
729
|
+
ulimits: list[Ulimit] | None = None,
|
|
730
|
+
init: bool | None = None,
|
|
731
|
+
log_config: LogConfig | None = None,
|
|
731
732
|
) -> str:
|
|
732
733
|
LOG.debug("Creating container with attributes: %s", locals())
|
|
733
734
|
extra_hosts = None
|
|
@@ -834,31 +835,31 @@ class SdkDockerClient(ContainerClient):
|
|
|
834
835
|
image_name: str,
|
|
835
836
|
stdin=None,
|
|
836
837
|
*,
|
|
837
|
-
name:
|
|
838
|
-
entrypoint:
|
|
838
|
+
name: str | None = None,
|
|
839
|
+
entrypoint: str | None = None,
|
|
839
840
|
remove: bool = False,
|
|
840
841
|
interactive: bool = False,
|
|
841
842
|
tty: bool = False,
|
|
842
843
|
detach: bool = False,
|
|
843
|
-
command:
|
|
844
|
-
volumes:
|
|
845
|
-
ports:
|
|
846
|
-
exposed_ports:
|
|
847
|
-
env_vars:
|
|
848
|
-
user:
|
|
849
|
-
cap_add:
|
|
850
|
-
cap_drop:
|
|
851
|
-
security_opt:
|
|
852
|
-
network:
|
|
853
|
-
dns:
|
|
854
|
-
additional_flags:
|
|
855
|
-
workdir:
|
|
856
|
-
labels:
|
|
857
|
-
platform:
|
|
858
|
-
privileged:
|
|
859
|
-
ulimits:
|
|
860
|
-
init:
|
|
861
|
-
log_config:
|
|
844
|
+
command: list[str] | str | None = None,
|
|
845
|
+
volumes: list[SimpleVolumeBind] | None = None,
|
|
846
|
+
ports: PortMappings | None = None,
|
|
847
|
+
exposed_ports: list[str] | None = None,
|
|
848
|
+
env_vars: dict[str, str] | None = None,
|
|
849
|
+
user: str | None = None,
|
|
850
|
+
cap_add: list[str] | None = None,
|
|
851
|
+
cap_drop: list[str] | None = None,
|
|
852
|
+
security_opt: list[str] | None = None,
|
|
853
|
+
network: str | None = None,
|
|
854
|
+
dns: str | None = None,
|
|
855
|
+
additional_flags: str | None = None,
|
|
856
|
+
workdir: str | None = None,
|
|
857
|
+
labels: dict[str, str] | None = None,
|
|
858
|
+
platform: DockerPlatform | None = None,
|
|
859
|
+
privileged: bool | None = None,
|
|
860
|
+
ulimits: list[Ulimit] | None = None,
|
|
861
|
+
init: bool | None = None,
|
|
862
|
+
log_config: LogConfig | None = None,
|
|
862
863
|
) -> tuple[bytes, bytes]:
|
|
863
864
|
LOG.debug("Running container with image: %s", image_name)
|
|
864
865
|
container = None
|
|
@@ -905,13 +906,13 @@ class SdkDockerClient(ContainerClient):
|
|
|
905
906
|
def exec_in_container(
|
|
906
907
|
self,
|
|
907
908
|
container_name_or_id: str,
|
|
908
|
-
command:
|
|
909
|
+
command: list[str] | str,
|
|
909
910
|
interactive=False,
|
|
910
911
|
detach=False,
|
|
911
|
-
env_vars:
|
|
912
|
-
stdin:
|
|
913
|
-
user:
|
|
914
|
-
workdir:
|
|
912
|
+
env_vars: dict[str, str | None] | None = None,
|
|
913
|
+
stdin: bytes | None = None,
|
|
914
|
+
user: str | None = None,
|
|
915
|
+
workdir: str | None = None,
|
|
915
916
|
) -> tuple[bytes, bytes]:
|
|
916
917
|
LOG.debug("Executing command in container %s: %s", container_name_or_id, command)
|
|
917
918
|
try:
|
|
@@ -938,7 +939,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
938
939
|
sock.shutdown(socket.SHUT_WR)
|
|
939
940
|
stdout, stderr = self._read_from_sock(sock, tty)
|
|
940
941
|
return stdout, stderr
|
|
941
|
-
except
|
|
942
|
+
except TimeoutError:
|
|
942
943
|
pass
|
|
943
944
|
else:
|
|
944
945
|
if detach:
|
|
@@ -959,7 +960,7 @@ class SdkDockerClient(ContainerClient):
|
|
|
959
960
|
except APIError as e:
|
|
960
961
|
raise ContainerException() from e
|
|
961
962
|
|
|
962
|
-
def login(self, username: str, password: str, registry:
|
|
963
|
+
def login(self, username: str, password: str, registry: str | None = None) -> None:
|
|
963
964
|
LOG.debug("Docker login for %s", username)
|
|
964
965
|
try:
|
|
965
966
|
self.client().login(username, password=password, registry=registry, reauth=True)
|
localstack/utils/diagnose.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import inspect
|
|
4
4
|
import os
|
|
5
5
|
import socket
|
|
6
|
-
from typing import Optional, Union
|
|
7
6
|
|
|
8
7
|
from localstack import config
|
|
9
8
|
from localstack.constants import DEFAULT_VOLUME_DIR
|
|
@@ -77,14 +76,14 @@ def get_localstack_config() -> dict:
|
|
|
77
76
|
return result
|
|
78
77
|
|
|
79
78
|
|
|
80
|
-
def inspect_main_container() ->
|
|
79
|
+
def inspect_main_container() -> str | dict:
|
|
81
80
|
try:
|
|
82
81
|
return DOCKER_CLIENT.inspect_container(get_main_container_name())
|
|
83
82
|
except Exception as e:
|
|
84
83
|
return f"inspect failed: {e}"
|
|
85
84
|
|
|
86
85
|
|
|
87
|
-
def get_localstack_version() -> dict[str,
|
|
86
|
+
def get_localstack_version() -> dict[str, str | None]:
|
|
88
87
|
return {
|
|
89
88
|
"build-date": os.environ.get("LOCALSTACK_BUILD_DATE"),
|
|
90
89
|
"build-git-hash": os.environ.get("LOCALSTACK_BUILD_GIT_HASH"),
|
localstack/utils/docker_utils.py
CHANGED
|
@@ -2,7 +2,6 @@ import functools
|
|
|
2
2
|
import logging
|
|
3
3
|
import platform
|
|
4
4
|
import random
|
|
5
|
-
from typing import Optional, Union
|
|
6
5
|
|
|
7
6
|
from localstack import config
|
|
8
7
|
from localstack.constants import DEFAULT_VOLUME_DIR, DOCKER_IMAGE_NAME
|
|
@@ -80,7 +79,7 @@ def inspect_current_container_mounts() -> list[VolumeInfo]:
|
|
|
80
79
|
|
|
81
80
|
|
|
82
81
|
@functools.lru_cache
|
|
83
|
-
def get_default_volume_dir_mount() ->
|
|
82
|
+
def get_default_volume_dir_mount() -> VolumeInfo | None:
|
|
84
83
|
"""
|
|
85
84
|
Returns the volume information of LocalStack's DEFAULT_VOLUME_DIR (/var/lib/localstack), if mounted,
|
|
86
85
|
else it returns None. If we're not currently in docker a VauleError is raised. in a container, a ValueError is
|
|
@@ -133,8 +132,8 @@ def get_host_path_for_path_in_docker(path):
|
|
|
133
132
|
|
|
134
133
|
|
|
135
134
|
def container_ports_can_be_bound(
|
|
136
|
-
ports:
|
|
137
|
-
address:
|
|
135
|
+
ports: IntOrPort | list[IntOrPort],
|
|
136
|
+
address: str | None = None,
|
|
138
137
|
) -> bool:
|
|
139
138
|
"""Determine whether a given list of ports can be bound by Docker containers
|
|
140
139
|
|
localstack/utils/functions.py
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import functools
|
|
4
4
|
import inspect
|
|
5
5
|
import logging
|
|
6
|
-
from
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import Any
|
|
7
8
|
|
|
8
9
|
LOG = logging.getLogger(__name__)
|
|
9
10
|
|
|
@@ -20,7 +21,7 @@ def run_safe(_python_lambda, *args, _default=None, **kwargs):
|
|
|
20
21
|
|
|
21
22
|
def call_safe(
|
|
22
23
|
func: Callable, args: tuple = None, kwargs: dict = None, exception_message: str = None
|
|
23
|
-
) ->
|
|
24
|
+
) -> Any | None:
|
|
24
25
|
"""
|
|
25
26
|
Call the given function with the given arguments, and if it fails, log the given exception_message.
|
|
26
27
|
If logging.DEBUG is set for the logger, then we also log the traceback.
|
localstack/utils/http.py
CHANGED
|
@@ -2,7 +2,6 @@ import logging
|
|
|
2
2
|
import math
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
|
-
from typing import Optional, Union
|
|
6
5
|
from urllib.parse import parse_qs, parse_qsl, urlencode, urlparse, urlunparse
|
|
7
6
|
|
|
8
7
|
import requests
|
|
@@ -54,7 +53,7 @@ def create_chunked_data(data, chunk_size: int = 80):
|
|
|
54
53
|
return ret
|
|
55
54
|
|
|
56
55
|
|
|
57
|
-
def canonicalize_headers(headers:
|
|
56
|
+
def canonicalize_headers(headers: dict | CaseInsensitiveDict) -> dict:
|
|
58
57
|
if not headers:
|
|
59
58
|
return headers
|
|
60
59
|
|
|
@@ -103,7 +102,7 @@ def add_query_params_to_url(uri: str, query_params: dict) -> str:
|
|
|
103
102
|
|
|
104
103
|
|
|
105
104
|
def make_http_request(
|
|
106
|
-
url: str, data:
|
|
105
|
+
url: str, data: bytes | str = None, headers: dict[str, str] = None, method: str = "GET"
|
|
107
106
|
) -> Response:
|
|
108
107
|
return requests.request(
|
|
109
108
|
url=url, method=method, headers=headers, data=data, auth=NetrcBypassAuth(), verify=False
|
|
@@ -179,7 +178,7 @@ def download(
|
|
|
179
178
|
path: str,
|
|
180
179
|
verify_ssl: bool = True,
|
|
181
180
|
timeout: float = None,
|
|
182
|
-
request_headers:
|
|
181
|
+
request_headers: dict | None = None,
|
|
183
182
|
quiet: bool = False,
|
|
184
183
|
) -> None:
|
|
185
184
|
"""Downloads file at url to the given path. Raises TimeoutError if the optional timeout (in secs) is reached.
|
|
@@ -291,7 +290,7 @@ def download_github_artifact(url: str, target_file: str, timeout: int = None):
|
|
|
291
290
|
Optionally allows to define a timeout in seconds."""
|
|
292
291
|
|
|
293
292
|
def do_download(
|
|
294
|
-
download_url: str, request_headers:
|
|
293
|
+
download_url: str, request_headers: dict | None = None, print_error: bool = False
|
|
295
294
|
):
|
|
296
295
|
try:
|
|
297
296
|
download(download_url, target_file, timeout=timeout, request_headers=request_headers)
|
localstack/utils/json.py
CHANGED
|
@@ -5,7 +5,7 @@ import logging
|
|
|
5
5
|
import os
|
|
6
6
|
from datetime import date, datetime
|
|
7
7
|
from json import JSONDecodeError
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
from localstack.config import HostAndPort
|
|
11
11
|
|
|
@@ -63,9 +63,9 @@ class FileMappedDocument(dict):
|
|
|
63
63
|
concurrent writes, run load(). To save and overwrite the current document on disk, run save().
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
|
-
path:
|
|
66
|
+
path: str | os.PathLike
|
|
67
67
|
|
|
68
|
-
def __init__(self, path:
|
|
68
|
+
def __init__(self, path: str | os.PathLike, mode=0o664):
|
|
69
69
|
super().__init__()
|
|
70
70
|
self.path = path
|
|
71
71
|
self.mode = mode
|
localstack/utils/net.py
CHANGED
|
@@ -5,7 +5,7 @@ import socket
|
|
|
5
5
|
import threading
|
|
6
6
|
from collections.abc import MutableMapping
|
|
7
7
|
from contextlib import closing
|
|
8
|
-
from typing import Any, NamedTuple
|
|
8
|
+
from typing import Any, NamedTuple
|
|
9
9
|
from urllib.parse import urlparse
|
|
10
10
|
|
|
11
11
|
import dns.resolver
|
|
@@ -50,14 +50,14 @@ class Port(NamedTuple):
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
# simple helper type to encapsulate int/Port argument types
|
|
53
|
-
IntOrPort =
|
|
53
|
+
IntOrPort = int | Port
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def is_port_open(
|
|
57
|
-
port_or_url:
|
|
57
|
+
port_or_url: int | str,
|
|
58
58
|
http_path: str = None,
|
|
59
59
|
expect_success: bool = True,
|
|
60
|
-
protocols:
|
|
60
|
+
protocols: str | list[str] | None = None,
|
|
61
61
|
quiet: bool = True,
|
|
62
62
|
):
|
|
63
63
|
from localstack.utils.http import safe_requests
|
|
@@ -275,7 +275,7 @@ def get_free_tcp_port_range(num_ports: int, max_attempts: int = 50) -> "PortRang
|
|
|
275
275
|
raise PortNotAvailableException("reached max_attempts when trying to find port range")
|
|
276
276
|
|
|
277
277
|
|
|
278
|
-
def resolve_hostname(hostname: str) ->
|
|
278
|
+
def resolve_hostname(hostname: str) -> str | None:
|
|
279
279
|
"""Resolve the given hostname and return its IP address, or None if it cannot be resolved."""
|
|
280
280
|
try:
|
|
281
281
|
return socket.gethostbyname(hostname)
|
|
@@ -362,7 +362,7 @@ class PortRange:
|
|
|
362
362
|
"""
|
|
363
363
|
return range(self.start, self.end + 1)
|
|
364
364
|
|
|
365
|
-
def reserve_port(self, port:
|
|
365
|
+
def reserve_port(self, port: IntOrPort | None = None, duration: int | None = None) -> int:
|
|
366
366
|
"""
|
|
367
367
|
Reserves the given port (if it is still free). If the given port is None, it reserves a free port from the
|
|
368
368
|
configured port range for external services. If a port is given, it has to be within the configured
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import logging
|
|
3
|
-
from typing import NoReturn
|
|
3
|
+
from typing import NoReturn
|
|
4
4
|
|
|
5
5
|
LOG = logging.getLogger(__name__)
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ class NoExitArgumentParser(argparse.ArgumentParser):
|
|
|
12
12
|
* ArgumentParser subclassing example: https://stackoverflow.com/a/59072378/6875981
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
def exit(self, status: int = ..., message:
|
|
15
|
+
def exit(self, status: int = ..., message: str | None = ...) -> NoReturn:
|
|
16
16
|
LOG.warning("Error in argument parser but preventing exit: %s", message)
|
|
17
17
|
|
|
18
18
|
def error(self, message: str) -> NoReturn:
|