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.
This commit is contained in:
+23
-5
@@ -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()
|
||||
.filter(entry -> isUsable(entry.getValue()))
|
||||
.filter(entry -> isUserDefinedNetwork(entry.getKey()))
|
||||
.findFirst()
|
||||
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())
|
||||
.or(() -> networks.entrySet().stream()
|
||||
.filter(entry -> isUsable(entry.getValue()))
|
||||
.findFirst())
|
||||
|
||||
+56
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user