testcontainers / testcontainers-java

Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
https://testcontainers.org
MIT License
8.05k stars 1.66k forks source link

[Feature]: Add a method to `GenericContainer` to expose a random container port with the same number as the host port #9553

Open linghengqian opened 6 days ago

linghengqian commented 6 days ago

Module

Core

Problem

Solution

Benefit

Alternatives

Would you like to help contributing this feature?

No

kiview commented 1 day ago

Hey @linghengqian, what you are trying to achieve there, should already work like this:

import org.apache.curator.test.InstanceSpec;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.FixedHostPortGenericContainer;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
public class ExampleTest {
    @Test
    void test() {
        Network network = Network.newNetwork();
        try (
                GenericContainer<?> zookeeper = new GenericContainer<>("zookeeper:3.9.3-jre-17")
                        .withNetwork(network)
                        .withNetworkAliases("foo")
                        .withExposedPorts(2181);
                GenericContainer<?> hiveServer2 = new GenericContainer<>("apache/hive:4.0.1")
                        .withNetwork(network)
                        .withEnv("SERVICE_NAME", "hiveserver2")
                        .withExposedPort(4711)
                        .dependsOn(zookeeper)
        ) {
            zookeeper.start();
            hiveServer2.withEnv("SERVICE_OPTS", "-Dhive.server2.support.dynamic.service.discovery=true" + " "
                    + "-Dhive.zookeeper.quorum=" + zookeeper.getNetworkAliases().get(0) + ":2181" + " "
                    + "-Dhive.server2.thrift.bind.host=0.0.0.0" + " "
                    + "-Dhive.server2.thrift.port=" + hiveServer2.getFirstMappedPort());
            hiveServer2.start();
        }
    }
}

I don't see why the port within the container needs to be the same and why the internal port needs to be random (I understand it can be arbitrary though).

linghengqian commented 1 day ago
hiveServer2.withEnv("SERVICE_OPTS", "-Dhive.server2.support.dynamic.service.discovery=true" + " "
                    + "-Dhive.zookeeper.quorum=" + zookeeper.getNetworkAliases().get(0) + ":2181" + " "
                    + "-Dhive.server2.thrift.bind.host=0.0.0.0" + " "
                    + "-Dhive.server2.thrift.port=" + hiveServer2.getFirstMappedPort());
hiveServer2.start();
  • @kiview I've wanted to do something similar before, but the problem is that testcontainers simply don't allow it. If I call hiveServer2.getFirstMappedPort() before calling hiveServer2.start(), this will throw an exception. Mapped port can only be obtained after the container is started
    java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
    at org.testcontainers.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:513)
    at org.testcontainers.containers.ContainerState.getMappedPort(ContainerState.java:161)
    at io.github.linghengqian.hive.server2.jdbc.driver.thin.ZookeeperServiceDiscoveryTest.assertShardingInLocalTransactions(ZookeeperServiceDiscoveryTest.java:75)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1597)
  • I'm not sure if I should pull the unit tests I wrote in https://github.com/linghengqian/hive-server2-jdbc-driver/pull/14 into a separate git, since this exception seems to be thrown intentionally by testcontainers.
eddumelendez commented 1 day ago

Hi, what you can try is create a custom script, see LocalStackContainer for reference.

https://github.com/testcontainers/testcontainers-java/blob/37b6f873fda84724538adddb28aea5323beeb548/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java#L126-L132

The script must be copied to the container when it is starting

https://github.com/testcontainers/testcontainers-java/blob/37b6f873fda84724538adddb28aea5323beeb548/modules/localstack/src/main/java/org/testcontainers/containers/localstack/LocalStackContainer.java#L204-L212

Hope that helps.

linghengqian commented 21 hours ago

I don't see why the port within the container needs to be the same and why the internal port needs to be random (I understand it can be arbitrary though).

eddumelendez commented 13 hours ago

the entrypoint in apache/hive image is /entrypoint.sh. What I am suggesting is override the entrypoint to create a custom one with /testcontainers_start.sh in order to create new env vars and calling /entrypoint.sh as part of the custom script for Hive initialization.

linghengqian commented 2 hours ago

the entrypoint in apache/hive image is /entrypoint.sh. What I am suggesting is override the entrypoint to create a custom one with /testcontainers_start.sh in order to create new env vars and calling /entrypoint.sh as part of the custom script for Hive initialization.