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:
+21
-3
@@ -5,6 +5,7 @@ import com.github.dockerjava.api.command.InspectContainerResponse;
|
|||||||
import com.github.dockerjava.api.model.ContainerNetwork;
|
import com.github.dockerjava.api.model.ContainerNetwork;
|
||||||
import com.github.dockerjava.api.model.ExposedPort;
|
import com.github.dockerjava.api.model.ExposedPort;
|
||||||
import com.github.dockerjava.api.model.Ports;
|
import com.github.dockerjava.api.model.Ports;
|
||||||
|
import io.github.hectorvent.floci.config.EmulatorConfig;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
@@ -32,14 +33,21 @@ public class CurrentContainerNetworkResolver {
|
|||||||
|
|
||||||
private final DockerClient dockerClient;
|
private final DockerClient dockerClient;
|
||||||
private final ContainerDetector containerDetector;
|
private final ContainerDetector containerDetector;
|
||||||
|
private final EmulatorConfig config;
|
||||||
private final Map<Integer, Integer> cachedPublishedPorts = new ConcurrentHashMap<>();
|
private final Map<Integer, Integer> cachedPublishedPorts = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private volatile Optional<CurrentContainerNetwork> cachedNetwork;
|
private volatile Optional<CurrentContainerNetwork> cachedNetwork;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CurrentContainerNetworkResolver(DockerClient dockerClient, ContainerDetector containerDetector) {
|
public CurrentContainerNetworkResolver(DockerClient dockerClient, ContainerDetector containerDetector,
|
||||||
|
EmulatorConfig config) {
|
||||||
this.dockerClient = dockerClient;
|
this.dockerClient = dockerClient;
|
||||||
this.containerDetector = containerDetector;
|
this.containerDetector = containerDetector;
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CurrentContainerNetworkResolver(DockerClient dockerClient, ContainerDetector containerDetector) {
|
||||||
|
this(dockerClient, containerDetector, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> resolveNetworkName() {
|
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) {
|
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 -> isUsable(entry.getValue()))
|
||||||
.filter(entry -> isUserDefinedNetwork(entry.getKey()))
|
.filter(entry -> isUserDefinedNetwork(entry.getKey()))
|
||||||
.findFirst()
|
.findFirst())
|
||||||
.or(() -> networks.entrySet().stream()
|
.or(() -> networks.entrySet().stream()
|
||||||
.filter(entry -> isUsable(entry.getValue()))
|
.filter(entry -> isUsable(entry.getValue()))
|
||||||
.findFirst())
|
.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.ContainerNetwork;
|
||||||
import com.github.dockerjava.api.model.ExposedPort;
|
import com.github.dockerjava.api.model.ExposedPort;
|
||||||
import com.github.dockerjava.api.model.Ports;
|
import com.github.dockerjava.api.model.Ports;
|
||||||
|
import io.github.hectorvent.floci.config.EmulatorConfig;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -43,6 +44,55 @@ class CurrentContainerNetworkResolverTest {
|
|||||||
assertEquals(Optional.of("172.24.0.2"), resolver.resolveContainerIp());
|
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
|
@Test
|
||||||
void resolveNetworkName_returnsEmptyOutsideContainer() {
|
void resolveNetworkName_returnsEmptyOutsideContainer() {
|
||||||
DockerClient dockerClient = mock(DockerClient.class);
|
DockerClient dockerClient = mock(DockerClient.class);
|
||||||
@@ -126,7 +176,12 @@ class CurrentContainerNetworkResolverTest {
|
|||||||
private final String containerId;
|
private final String containerId;
|
||||||
|
|
||||||
TestResolver(DockerClient dockerClient, ContainerDetector containerDetector, 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;
|
this.containerId = containerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user