diff --git a/docker-compose-pro.yml b/docker-compose-pro.yml index 3228f05247af8..ae25b828c8f70 100644 --- a/docker-compose-pro.yml +++ b/docker-compose-pro.yml @@ -15,6 +15,7 @@ services: - PERSISTENCE=${PERSISTENCE-} - LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY-} # required for Pro - DOCKER_HOST=unix:///var/run/docker.sock + # - USE_MANAGED_DOCKER_NETWORK=1 # allow LocalStack to manage networking volumes: - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" - "/var/run/docker.sock:/var/run/docker.sock" diff --git a/docker-compose.yml b/docker-compose.yml index b19fcaf4da458..eb67d01d7acbe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: environment: - DEBUG=${DEBUG-} - DOCKER_HOST=unix:///var/run/docker.sock + # - USE_MANAGED_DOCKER_NETWORK=1 # allow LocalStack to manage networking volumes: - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" - "/var/run/docker.sock:/var/run/docker.sock" diff --git a/localstack/config.py b/localstack/config.py index b9520061e8ad8..066e3174d1d08 100644 --- a/localstack/config.py +++ b/localstack/config.py @@ -819,6 +819,13 @@ def get_gateway_listen(gateway_listen: str) -> List[HostAndPort]: os.environ.get("BUCKET_MARKER_LOCAL", "").strip() or DEFAULT_BUCKET_MARKER_LOCAL ) +# Create managed docker network +USE_MANAGED_DOCKER_NETWORK = is_env_true("USE_MANAGED_DOCKER_NETWORK") +MANAGED_DOCKER_NETWORK_NAME = os.environ.get( + "MANAGED_DOCKER_NETWORK_NAME", + "lsnet", +) + # PUBLIC: bridge (Docker default) # Docker network driver for the Lambda and ECS containers. https://docs.docker.com/network/ LAMBDA_DOCKER_NETWORK = os.environ.get("LAMBDA_DOCKER_NETWORK", "").strip() @@ -1120,6 +1127,7 @@ def get_gateway_listen(gateway_listen: str) -> List[HostAndPort]: "LS_LOG", "MAIN_CONTAINER_NAME", "MAIN_DOCKER_NETWORK", + "MANAGED_DOCKER_NETWORK_NAME", "OPENSEARCH_ENDPOINT_STRATEGY", "OUTBOUND_HTTP_PROXY", "OUTBOUND_HTTPS_PROXY", @@ -1147,6 +1155,7 @@ def get_gateway_listen(gateway_listen: str) -> List[HostAndPort]: "TEST_IAM_USER_ID", "TEST_IAM_USER_NAME", "TF_COMPAT_MODE", + "USE_MANAGED_DOCKER_NETWORK", "USE_SINGLE_REGION", "USE_SSL", "WAIT_FOR_DEBUGGER", @@ -1312,3 +1321,8 @@ def init_directories() -> Directories: # initialize directories dirs: Directories dirs = init_directories() + +# merge docker networks if required +if USE_MANAGED_DOCKER_NETWORK: + LAMBDA_DOCKER_NETWORK = MANAGED_DOCKER_NETWORK_NAME + MAIN_DOCKER_NETWORK = MANAGED_DOCKER_NETWORK_NAME diff --git a/localstack/utils/bootstrap.py b/localstack/utils/bootstrap.py index ff6280ec5c03b..ea503823b25c7 100644 --- a/localstack/utils/bootstrap.py +++ b/localstack/utils/bootstrap.py @@ -16,6 +16,7 @@ from localstack.utils.container_networking import get_main_container_name from localstack.utils.container_utils.container_client import ( ContainerException, + NoSuchNetwork, PortMappings, SimpleVolumeBind, VolumeBind, @@ -554,6 +555,10 @@ def configure_container(container: LocalstackContainer): # mount docker socket container.volumes.append((config.DOCKER_SOCK, config.DOCKER_SOCK)) + # if using managed networking, set up the docker network + if config.USE_MANAGED_DOCKER_NETWORK: + setup_managed_docker_network(container) + container.privileged = True @@ -561,6 +566,18 @@ def configure_volume_mounts(container: LocalstackContainer): container.volumes.add(VolumeBind(config.VOLUME_DIR, DEFAULT_VOLUME_DIR)) +def setup_managed_docker_network(container: LocalstackContainer): + ensure_docker_network(config.MANAGED_DOCKER_NETWORK_NAME) + container.network = config.MANAGED_DOCKER_NETWORK_NAME + + +def ensure_docker_network(network_name: str): + try: + DOCKER_CLIENT.inspect_network(network_name) + except NoSuchNetwork: + DOCKER_CLIENT.create_network(network_name) + + @log_duration() def prepare_host(console): """ diff --git a/localstack/utils/container_utils/docker_cmd_client.py b/localstack/utils/container_utils/docker_cmd_client.py index c275c6b79566e..85334e8871be6 100644 --- a/localstack/utils/container_utils/docker_cmd_client.py +++ b/localstack/utils/container_utils/docker_cmd_client.py @@ -395,7 +395,7 @@ def _inspect_object(self, object_name_or_id: str) -> Dict[str, Union[dict, list, cmd = self._docker_cmd() cmd += ["inspect", "--format", "{{json .}}", object_name_or_id] try: - cmd_result = run(cmd) + cmd_result = run(cmd, stderr=subprocess.PIPE) except subprocess.CalledProcessError as e: # note: case-insensitive comparison, to support Docker and Podman output formats if "no such object" in to_str(e.stdout).lower():