Merge pull request 'fix(docker): use Floci's address on the configured docker network' (#1) from fix/prefer-configured-docker-network into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-10-24 05:43:01 +00:00
2 changed files with 79 additions and 6 deletions
@@ -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<Integer, Integer> cachedPublishedPorts = new ConcurrentHashMap<>();
private volatile Optional<CurrentContainerNetwork> 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<String> 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<CurrentContainerNetwork> selectNetwork(Map<String, ContainerNetwork> networks) {
return networks.entrySet().stream()
Optional<String> 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()
.findFirst())
.or(() -> networks.entrySet().stream()
.filter(entry -> isUsable(entry.getValue()))
.findFirst())
@@ -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<String, ContainerNetwork> 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<String, ContainerNetwork> 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;
}