From a1510d8cd6f44ff8cac9ec76a20f336272b61fe2 Mon Sep 17 00:00:00 2001 From: Zakaria Date: Sat, 24 Oct 2026 01:35:31 -0400 Subject: [PATCH] fix(docker): use Floci's address on the configured docker network Spawned containers (Lambda, RDS, EKS, EC2, ...) join services.docker-network, but the address they were given to reach Floci was its IP on the first user-defined network it is attached to. Once Floci is also attached to an emulated VPC network, that address is unreachable and every Lambda times out with "Failed to get next invocation". Prefer the configured network when Floci is attached to it. --- .../CurrentContainerNetworkResolver.java | 28 +++++++-- .../CurrentContainerNetworkResolverTest.java | 57 ++++++++++++++++++- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolver.java b/src/main/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolver.java index ad2679f..3e80186 100644 --- a/src/main/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolver.java +++ b/src/main/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolver.java @@ -5,6 +5,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Ports; +import io.github.hectorvent.floci.config.EmulatorConfig; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.jboss.logging.Logger; @@ -32,14 +33,21 @@ public class CurrentContainerNetworkResolver { private final DockerClient dockerClient; private final ContainerDetector containerDetector; + private final EmulatorConfig config; private final Map cachedPublishedPorts = new ConcurrentHashMap<>(); private volatile Optional cachedNetwork; @Inject - public CurrentContainerNetworkResolver(DockerClient dockerClient, ContainerDetector containerDetector) { + public CurrentContainerNetworkResolver(DockerClient dockerClient, ContainerDetector containerDetector, + EmulatorConfig config) { this.dockerClient = dockerClient; this.containerDetector = containerDetector; + this.config = config; + } + + public CurrentContainerNetworkResolver(DockerClient dockerClient, ContainerDetector containerDetector) { + this(dockerClient, containerDetector, null); } public Optional resolveNetworkName() { @@ -148,11 +156,21 @@ public class CurrentContainerNetworkResolver { } } + /** + * Spawned containers join services.docker-network when it is set, so Floci's address on that + * network is the one they can reach. Floci can be attached to other networks too (for example + * the emulated VPC networks), whose addresses spawned containers cannot route to. + */ private Optional selectNetwork(Map networks) { - return networks.entrySet().stream() - .filter(entry -> isUsable(entry.getValue())) - .filter(entry -> isUserDefinedNetwork(entry.getKey())) - .findFirst() + Optional configured = config == null ? Optional.empty() + : config.services().dockerNetwork().filter(name -> !name.isBlank()); + return configured + .filter(name -> isUsable(networks.get(name))) + .map(name -> Map.entry(name, networks.get(name))) + .or(() -> networks.entrySet().stream() + .filter(entry -> isUsable(entry.getValue())) + .filter(entry -> isUserDefinedNetwork(entry.getKey())) + .findFirst()) .or(() -> networks.entrySet().stream() .filter(entry -> isUsable(entry.getValue())) .findFirst()) diff --git a/src/test/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolverTest.java b/src/test/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolverTest.java index 34f4187..d159d76 100644 --- a/src/test/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolverTest.java +++ b/src/test/java/io/github/hectorvent/floci/core/common/docker/CurrentContainerNetworkResolverTest.java @@ -6,6 +6,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.Ports; +import io.github.hectorvent.floci.config.EmulatorConfig; import org.junit.jupiter.api.Test; import java.util.LinkedHashMap; @@ -43,6 +44,55 @@ class CurrentContainerNetworkResolverTest { assertEquals(Optional.of("172.24.0.2"), resolver.resolveContainerIp()); } + @Test + void resolveNetworkName_prefersConfiguredDockerNetworkOverOtherAttachedNetworks() { + DockerClient dockerClient = mock(DockerClient.class); + ContainerDetector containerDetector = mock(ContainerDetector.class); + when(containerDetector.isRunningInContainer()).thenReturn(true); + + InspectContainerCmd inspectCmd = mock(InspectContainerCmd.class); + InspectContainerResponse inspect = mock(InspectContainerResponse.class, RETURNS_DEEP_STUBS); + Map networks = networks( + "floci-vpc-4566-us-east-1-vpc-default-us-east-1", "172.31.0.3", + "floci_default", "172.22.0.4"); + when(dockerClient.inspectContainerCmd("floci-container")).thenReturn(inspectCmd); + when(inspectCmd.exec()).thenReturn(inspect); + when(inspect.getNetworkSettings().getNetworks()).thenReturn(networks); + + EmulatorConfig config = mock(EmulatorConfig.class, RETURNS_DEEP_STUBS); + when(config.services().dockerNetwork()).thenReturn(Optional.of("floci_default")); + + CurrentContainerNetworkResolver resolver = + new TestResolver(dockerClient, containerDetector, config, "floci-container"); + + assertEquals(Optional.of("floci_default"), resolver.resolveNetworkName()); + assertEquals(Optional.of("172.22.0.4"), resolver.resolveContainerIp()); + } + + @Test + void resolveNetworkName_fallsBackWhenConfiguredNetworkIsNotAttached() { + DockerClient dockerClient = mock(DockerClient.class); + ContainerDetector containerDetector = mock(ContainerDetector.class); + when(containerDetector.isRunningInContainer()).thenReturn(true); + + InspectContainerCmd inspectCmd = mock(InspectContainerCmd.class); + InspectContainerResponse inspect = mock(InspectContainerResponse.class, RETURNS_DEEP_STUBS); + Map networks = networks( + "bridge", "172.17.0.2", + "avoxx-network", "172.24.0.2"); + when(dockerClient.inspectContainerCmd("floci-container")).thenReturn(inspectCmd); + when(inspectCmd.exec()).thenReturn(inspect); + when(inspect.getNetworkSettings().getNetworks()).thenReturn(networks); + + EmulatorConfig config = mock(EmulatorConfig.class, RETURNS_DEEP_STUBS); + when(config.services().dockerNetwork()).thenReturn(Optional.of("floci_default")); + + CurrentContainerNetworkResolver resolver = + new TestResolver(dockerClient, containerDetector, config, "floci-container"); + + assertEquals(Optional.of("avoxx-network"), resolver.resolveNetworkName()); + } + @Test void resolveNetworkName_returnsEmptyOutsideContainer() { DockerClient dockerClient = mock(DockerClient.class); @@ -126,7 +176,12 @@ class CurrentContainerNetworkResolverTest { private final String containerId; TestResolver(DockerClient dockerClient, ContainerDetector containerDetector, String containerId) { - super(dockerClient, containerDetector); + this(dockerClient, containerDetector, null, containerId); + } + + TestResolver(DockerClient dockerClient, ContainerDetector containerDetector, EmulatorConfig config, + String containerId) { + super(dockerClient, containerDetector, config); this.containerId = containerId; } -- 2.52.0