From 4bead3801af2dd4cdf31630ff87b9640176bcb3a Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Wed, 7 May 2025 14:03:32 +0100 Subject: [PATCH 1/4] WIP: docker registry customisation --- localstack-core/localstack/config.py | 2 ++ .../utils/container_utils/container_client.py | 12 ++++++++++++ .../utils/container_utils/docker_sdk_client.py | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/localstack-core/localstack/config.py b/localstack-core/localstack/config.py index 5c2af11762fb4..ccd943b0f4a46 100644 --- a/localstack-core/localstack/config.py +++ b/localstack-core/localstack/config.py @@ -1007,6 +1007,8 @@ def populate_edge_configuration( # b) json dict mapping the to an image, e.g. {"python3.9": "custom-repo/lambda-py:thon3.9"} LAMBDA_RUNTIME_IMAGE_MAPPING = os.environ.get("LAMBDA_RUNTIME_IMAGE_MAPPING", "").strip() +DOCKER_GLOBAL_IMAGE_PREFIX = os.environ.get("DOCKER_GLOBAL_IMAGE_PREFIX", "").strip() + # PUBLIC: 0 (default) # Whether to disable usage of deprecated runtimes LAMBDA_RUNTIME_VALIDATION = int(os.environ.get("LAMBDA_RUNTIME_VALIDATION") or 0) diff --git a/localstack-core/localstack/utils/container_utils/container_client.py b/localstack-core/localstack/utils/container_utils/container_client.py index e05fdd6da5a55..9c31e565b5728 100644 --- a/localstack-core/localstack/utils/container_utils/container_client.py +++ b/localstack-core/localstack/utils/container_utils/container_client.py @@ -589,9 +589,21 @@ class DockerRunFlags: dns: Optional[List[str]] +class RegistryResolverStrategy(Protocol): + def resolve(self, image_name: str) -> str: ... + + +class HardCodedResolver: + def resolve(self, image_name: str) -> str: # noqa + return image_name + + # TODO: remove Docker/Podman compatibility switches (in particular strip_wellknown_repo_prefixes=...) # from the container client base interface and introduce derived Podman client implementations instead! class ContainerClient(metaclass=ABCMeta): + def __init__(self): + self.registry_resolver_strategy: RegistryResolverStrategy = HardCodedResolver() + @abstractmethod def get_system_info(self) -> dict: """Returns the docker system-wide information as dictionary (``docker info``).""" diff --git a/localstack-core/localstack/utils/container_utils/docker_sdk_client.py b/localstack-core/localstack/utils/container_utils/docker_sdk_client.py index de69fd101c56e..392fb5ced65f9 100644 --- a/localstack-core/localstack/utils/container_utils/docker_sdk_client.py +++ b/localstack-core/localstack/utils/container_utils/docker_sdk_client.py @@ -337,6 +337,8 @@ def copy_from_container( def pull_image(self, docker_image: str, platform: Optional[DockerPlatform] = None) -> None: LOG.debug("Pulling Docker image: %s", docker_image) # some path in the docker image string indicates a custom repository + + docker_image = self.registry_resolver_strategy.resolve(docker_image) try: self.client().images.pull(docker_image, platform=platform) except ImageNotFound: @@ -778,6 +780,8 @@ def create_container( if volumes: mounts = Util.convert_mount_list_to_dict(volumes) + image_name = self.registry_resolver_strategy.resolve(image_name) + def create_container(): return self.client().containers.create( image=image_name, From 532535325f84ab24072446e68bfcc3369dd63371 Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Wed, 7 May 2025 14:37:00 +0100 Subject: [PATCH 2/4] Call superclass constructor --- .../localstack/utils/container_utils/docker_sdk_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/localstack-core/localstack/utils/container_utils/docker_sdk_client.py b/localstack-core/localstack/utils/container_utils/docker_sdk_client.py index 392fb5ced65f9..0d841355daa82 100644 --- a/localstack-core/localstack/utils/container_utils/docker_sdk_client.py +++ b/localstack-core/localstack/utils/container_utils/docker_sdk_client.py @@ -59,6 +59,7 @@ class SdkDockerClient(ContainerClient): docker_client: Optional[DockerClient] def __init__(self): + super().__init__() try: self.docker_client = self._create_client() logging.getLogger("urllib3").setLevel(logging.INFO) From 069cfcf3f179e7e79be06a7196d8495688cc94fa Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Thu, 15 May 2025 15:21:10 +0100 Subject: [PATCH 3/4] Move config to ext --- localstack-core/localstack/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/localstack-core/localstack/config.py b/localstack-core/localstack/config.py index ccd943b0f4a46..c7986b22daa3f 100644 --- a/localstack-core/localstack/config.py +++ b/localstack-core/localstack/config.py @@ -1007,7 +1007,6 @@ def populate_edge_configuration( # b) json dict mapping the to an image, e.g. {"python3.9": "custom-repo/lambda-py:thon3.9"} LAMBDA_RUNTIME_IMAGE_MAPPING = os.environ.get("LAMBDA_RUNTIME_IMAGE_MAPPING", "").strip() -DOCKER_GLOBAL_IMAGE_PREFIX = os.environ.get("DOCKER_GLOBAL_IMAGE_PREFIX", "").strip() # PUBLIC: 0 (default) # Whether to disable usage of deprecated runtimes From b2bd45d18399a9b4a37bec73317bb56e2b6ecacb Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Fri, 30 May 2025 20:54:51 +0100 Subject: [PATCH 4/4] experiment: try class attribute --- .../localstack/utils/container_utils/container_client.py | 3 +-- .../localstack/utils/container_utils/docker_cmd_client.py | 4 ++++ .../localstack/utils/container_utils/docker_sdk_client.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/localstack-core/localstack/utils/container_utils/container_client.py b/localstack-core/localstack/utils/container_utils/container_client.py index 9c31e565b5728..fb880ba50f71c 100644 --- a/localstack-core/localstack/utils/container_utils/container_client.py +++ b/localstack-core/localstack/utils/container_utils/container_client.py @@ -601,8 +601,7 @@ def resolve(self, image_name: str) -> str: # noqa # TODO: remove Docker/Podman compatibility switches (in particular strip_wellknown_repo_prefixes=...) # from the container client base interface and introduce derived Podman client implementations instead! class ContainerClient(metaclass=ABCMeta): - def __init__(self): - self.registry_resolver_strategy: RegistryResolverStrategy = HardCodedResolver() + registry_resolver_strategy: RegistryResolverStrategy = HardCodedResolver() @abstractmethod def get_system_info(self) -> dict: diff --git a/localstack-core/localstack/utils/container_utils/docker_cmd_client.py b/localstack-core/localstack/utils/container_utils/docker_cmd_client.py index 7cdd7b59f8092..ac50a195bf38b 100644 --- a/localstack-core/localstack/utils/container_utils/docker_cmd_client.py +++ b/localstack-core/localstack/utils/container_utils/docker_cmd_client.py @@ -356,6 +356,7 @@ def copy_from_container( def pull_image(self, docker_image: str, platform: Optional[DockerPlatform] = None) -> None: cmd = self._docker_cmd() + docker_image = self.registry_resolver_strategy.resolve(docker_image) cmd += ["pull", docker_image] if platform: cmd += ["--platform", platform] @@ -518,6 +519,7 @@ def inspect_image( pull: bool = True, strip_wellknown_repo_prefixes: bool = True, ) -> Dict[str, Union[dict, list, str]]: + image_name = self.registry_resolver_strategy.resolve(image_name) try: result = self._inspect_object(image_name) if strip_wellknown_repo_prefixes: @@ -656,6 +658,7 @@ def has_docker(self) -> bool: return False def create_container(self, image_name: str, **kwargs) -> str: + image_name = self.registry_resolver_strategy.resolve(image_name) cmd, env_file = self._build_run_create_cmd("create", image_name, **kwargs) LOG.debug("Create container with cmd: %s", cmd) try: @@ -674,6 +677,7 @@ def create_container(self, image_name: str, **kwargs) -> str: Util.rm_env_vars_file(env_file) def run_container(self, image_name: str, stdin=None, **kwargs) -> Tuple[bytes, bytes]: + image_name = self.registry_resolver_strategy.resolve(image_name) cmd, env_file = self._build_run_create_cmd("run", image_name, **kwargs) LOG.debug("Run container with cmd: %s", cmd) try: diff --git a/localstack-core/localstack/utils/container_utils/docker_sdk_client.py b/localstack-core/localstack/utils/container_utils/docker_sdk_client.py index 0d841355daa82..a2b8f8a5f6746 100644 --- a/localstack-core/localstack/utils/container_utils/docker_sdk_client.py +++ b/localstack-core/localstack/utils/container_utils/docker_sdk_client.py @@ -59,7 +59,6 @@ class SdkDockerClient(ContainerClient): docker_client: Optional[DockerClient] def __init__(self): - super().__init__() try: self.docker_client = self._create_client() logging.getLogger("urllib3").setLevel(logging.INFO) @@ -468,6 +467,7 @@ def inspect_image( pull: bool = True, strip_wellknown_repo_prefixes: bool = True, ) -> Dict[str, Union[dict, list, str]]: + image_name = self.registry_resolver_strategy.resolve(image_name) try: result = self.client().images.get(image_name).attrs if strip_wellknown_repo_prefixes: