ContainerConnectionDetailsFactory.getContainer() is doing

if (this.container instanceof Startable startable) {
                startable.start();
            }

While TestcontainersLifecycleBeanPostProcessor.initializeStartables(...) already started the MockServer

Comment From: philwebb

Is this causing an issue? Can you provide a sample application showing the actual problem you're facing?

Comment From: marcantoine-bibeau

I do not think it creates a big problem but still created a regression on my side because I'm wrapping the MockServer and override the start() method to perform additional stuff. I can fix by verifying if the TestContainer is running but would be ideal to not start twice?

Comment From: snicoll

Can you please share the sample we've requested? See also https://github.com/spring-projects/spring-boot/wiki/How-To-Get-Help

Comment From: wilkinsona

We could perhaps use a non-null response from ContainerState#getContainerId() to determine that a container's already been started (as ContainerState#getMappedPort(int) does) but that won't work for every Startable implementation. Given this, it appears to be impossible for us to tell with 100% certainty if a container's already been started. @marcantoine-bibeau, I'd encourage you to implement start() such that it is idempotent as it is in GenericContainer.

Comment From: nosan

@wilkinsona Is it possible to add the following condition?

protected final C getContainer() {
    Assert.state(this.container != null,
            "Container cannot be obtained before the connection details bean has been initialized");
    if (!this.container.isRunning() && this.container instanceof Startable startable) {
        startable.start();
    }
    return this.container;
}

org.testcontainers.containers.ContainerState contains the following default methods:



    /**
     * @return is the container currently running?
     */
    default boolean isRunning() {
        if (getContainerId() == null) {
            return false;
        }

        try {
            Boolean running = getCurrentContainerInfo().getState().getRunning();
            return Boolean.TRUE.equals(running);
        } catch (DockerException e) {
            return false;
        }
    }

    /**
     * @return is the container created?
     */
    default boolean isCreated() {
        if (getContainerId() == null) {
            return false;
        }

        try {
            String status = getCurrentContainerInfo().getState().getStatus();
            return "created".equalsIgnoreCase(status) || isRunning();
        } catch (DockerException e) {
            return false;
        }
    }

Comment From: wilkinsona

Yes, that's what I was (poorly) describing above when talking about the container ID. It'll certainly help, but I'm not sure that it'll address the problem 100% of the time as there appears to be an implied assumption in Testcontainers that start() will be idempotent. If you've only got a Startable and start() isn't idempotent, I think it's possible that something will be started twice either by Boot or by a combination of Boot and Testcontainers.

Comment From: marcantoine-bibeau

That I was not aware, I thought that start() would only be called once. I'll definitely do that on my side! Feel free to improve on Spring side as you describe :) Thanks a lot for your help and quick response! I really appreciate! Cheers!

Comment From: philwebb

We might be able to track beans that we've started ourselves rather than relying on the container ID

Comment From: philwebb

I spent a fair bit of time looking at this today and I don't think tracking beans will work. The problem is that the container may have been started by org.testcontainers.junit.jupiter.TestcontainersExtension and there's no way for us to track that.