I have such test setup:
MyService
connects toPostgtreSQL
MyService
endpoint is being called from test suite
Both MyService
and PostgreSQL
are being run with Testcontainers.
Here is the network schema I want to achieve.
At first I tried to arrange communication by exposing ports.
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_VERSION));
static final GenericContainer<?> myService = new GenericContainer<>(DockerImageName.parse(MY_SERVICE_IMAGE))
.withEnv(
Map.of(
"SPRING_DATASOURCE_URL", postgres.getJdbcUrl(),
"SPRING_DATASOURCE_USERNAME", postgres.getUsername(),
"SPRING_DATASOURCE_PASSWORD", postgres.getPassword()
)
)
.withExposedPorts(8080)
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("MyService")))
According to logs MyService
couldn't establish connection to PostgreSQL
.
Caused by: java.net.ConnectException: Connection refused
Then I configured both services to share the same network.
static final Network SHARED_NETWORK = Network.newNetwork();
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_VERSION))
.withNetwork(SHARED_NETWORK)
.withNetworkAliases("postgres");
static final GenericContainer<?> myService = new GenericContainer<>(DockerImageName.parse(MY_SERVICE_IMAGE))
.withEnv(
Map.of(
"SPRING_DATASOURCE_URL", "jdbc:postgresql://postgres:5432/" postgres.getDatabaseName(),
"SPRING_DATASOURCE_USERNAME", postgres.getUsername(),
"SPRING_DATASOURCE_PASSWORD", postgres.getPassword()
)
)
.withExposedPorts(8080)
.withNetwork(SHARED_NETWORK)
.withNetworkAliases("MyService")
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("MyService")))
Now MyService
has established connection with PostgreSQL
successfully. But when I perform HTTP request to MyService
from the test suite, I get the same error.
restTemplate.getForObject("http://" myService.getHost() ":" myService.getMappedPort(8080) "/api/endpoint", Void.class)
Caused by: java.net.ConnectException: Connection refused
My question is how can I setup the containers network to make this architecture work?
CodePudding user response:
You need to specify port bindings to expose a port to the "outside world".
Example similar to what you want:
Network network = Network.newNetwork();
GenericContainer mariaDbServer = getMariaDbContainer(network);
GenericContainer flywayRunner = getFlywayContainer(network);
...
@SuppressWarnings("rawtypes")
private GenericContainer getMariaDbContainer(Network network) {
return new GenericContainer<>("mariadb:10.4.21-focal")
.withEnv(Map.of("MYSQL_ROOT_PASSWORD", "password", "MYSQL_DATABASE", "somedatabase"))
.withCommand(
"mysqld", "--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4",
"--collation-server=utf8mb4_unicode_ci").withNetwork(network).withNetworkAliases("somedatabasedb")
.withNetworkMode(network.getId())
.withExposedPorts(3306).withCreateContainerCmdModifier(
cmd -> cmd.withNetworkMode(network.getId()).withHostConfig(
new HostConfig()
.withPortBindings(new PortBinding(Ports.Binding.bindPort(20306), new ExposedPort(3306))))
.withNetworkMode(network.getId())).withStartupTimeout(Duration.ofMinutes(2L));
}
@SuppressWarnings("rawtypes")
private GenericContainer getFlywayContainer(Network network) {
return new GenericContainer<>("flyway/flyway:7.15.0-alpine")
.withEnv(Map.of("MYSQL_ROOT_PASSWORD", "password", "MYSQL_DATABASE", "somedatabase"))
.withCommand(
"-url=jdbc:mariadb://somedatabasedb -schemas=somedatabase-user=root -password=password -connectRetries=300 migrate")
.withFileSystemBind(Paths.get(".", "infrastructure/database/schema").toAbsolutePath().toString(),
"/flyway/sql", BindMode.READ_ONLY).withNetwork(network).waitingFor(
Wait.forLogMessage(".*Successfully applied.*", 1)
).withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS));
}
Container two communicates with container one using "internal" port.
Container one exposes 20306 (that redirects to 3306) port to the "outside world".