d.braun1991
d.braun1991

Reputation: 515

Testcontainers container.getHost() is behaving differently Locally vs. GitLab pipeline

tl:dr;

Testcontainers container.getHost() behaves differently in maven lifecycle step and GitLab CI-Pipeline.

Context:

Used:

Versions:

Why RestClient & Rest Template?

Although RestClient it considered state of the art compared to RestTemplate (spring.io: April 2024) ...

... the two are giving different error feedback (as follows).

Setup

Assume 3 java Microservices in a Junit-Testcontainers setup like:

Container1 <- Container2 <- Validator

Container2 needs Container1 and a Validator lifts both in an Junit-Testcontainers environment, watching its communication. Validator is not a live system, just an integrationtest-system.

Accordingly:

  1. Container1 is started
  2. Container1's port is written into Container2's env.
  3. Container2 is started.
  4. During startup Container2 tries to contact Container1's actuator endpoint.
    // Create a common network for all containers
    @ClassRule
    public static Network network = Network.newNetwork();

    @ClassRule
    public static GenericContainer<?> C1 =
            new GenericContainer<>(C1DockerImage)
                    .withExposedPorts(C1Port)
                    .withNetwork(network)
                    .withNetworkAliases("C1")

    @ClassRule
    public static GenericContainer<?> C2 =
            new GenericContainer<>(C2DockerImage)
                    .withExposedPorts(C2Port)
                    .withNetwork(network)
                    .withNetworkAliases("C2")

    static {
        // Start C1
        C1.start();
        C1BaseURL = "http://" + C1.getHost() + ":" + C1.getMappedPort(C1Port);

        // Start C2
        C2.withEnv("C1_URL", C1BaseURL);
        C2.start();

        logger.info("C1-Url given to C2 via ENV-Variable:   " + C1BaseURL);
    }

Accordingly:

So far -> so good :)

Behaviour

Three (3) environments are distinguished:

C2's Junit Test

Here a Testcontainers environment is started with C1 inside. Be aware, C2 is running in IntelliJ Maven Test livecycle Hence, C2 is not in an explicit Docker-Container.

C1's exposed port is wrapped by testcontainers and the proxy-port it given to C2. C2 connects and everything is fine. It works like a charm - just as expected.

Validator's junit test

Next, the Validator runs its IntelliJ Maven Test livecycle. Hence, C2 is now inside an explicit Docker-Container.

The URL given to C2 remains a pointer to http://localhost:TestcontainersProxyPortToC1.

Assumption:

The assumption is fulfilled:

GitLab CI-Pipeline

Within the GitLab CI-pipeline using the same Repository, the same Java-code, reproducable ...

The same code succeeds in the GitLab CI-Pipeline because localhost is replaced with the actual machines HostIP.

Confusion

My assumption is that both ..

.. have to behave in the same manner. Both C2-containers shall succeed or fail connecting via one of ..

Any other suggestionshow to fix this?

Is there annother Testcontainers-command, which always resolves the HostIP-Address?

Any help is appreciated :)

Upvotes: 1

Views: 384

Answers (1)

d.braun1991
d.braun1991

Reputation: 515

Problem is still existent ...

Is there annother Testcontainers-command, which always resolves the HostIP-Address?

AFAIK there is no such 'Testcontainers-command' ...

Divergent behaviour on C1.getHost() remains (April 2024).

  • local: C1.getHost() => "localhost"
  • Gitlab: C1.getHost() => "HostIP"

There is no clue, how C1.getHost() determines when which answer has to be delivered.

... Workaround

The localhost's IP is LOCALLY always the Testcontainers HostIP.

But in the GitLab Ci-Pipeline the IP differs from the HostIP. Hence C1's IP is for GitLab always a DIFFERENT HostIP in contrast to the runner.

Consequently the following changes in previously given code have to be made:

        C1BaseURL =
                "http://" + getHostIp(C1) + ":" + C1.getMappedPort(C1Port);

The custom function looks likt this:

    private static String getHostIp(GenericContainer<?> C1) {

        if (C1.getHost().equals("localhost")) {
            try {
                return InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                throw new RuntimeException(e);
            }
        }
        return C1.getHost();
    }

Still no success?

You may need to dig deeper: Getting the IP address of the current machine using Java

Upvotes: 1

Related Questions