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; }