Starting axon server using Testcontainers from a SpringBootTest -> port conflict

Hi all,

I’d like to use axon server in my integration tests (actually a cucumber steps definition). So I’am configuring a org.testcontainers.containers.GenericContainer to start the axon server docker image, which works fine as the log shows:

2019-08-09 17:52:55.981 INFO 7200 — [ main] :whale: [axoniq/axonserver:4.1.7] : Creating container for image: axoniq/axonserver:4.1.7
2019-08-09 17:52:56.020 INFO 7200 — [ main] o.t.utility.RegistryAuthLocator : Credentials not found for host (index.docker.io) when using credential helper/store (docker-credential-desktop)
2019-08-09 17:52:56.138 INFO 7200 — [ main] :whale: [axoniq/axonserver:4.1.7] : Starting container with ID: 7491a169dc40e54dfc8923e791c74c633846401dc679ddedddadc9f48e26344c
2019-08-09 17:52:56.607 INFO 7200 — [ main] :whale: [axoniq/axonserver:4.1.7] : Container axoniq/axonserver:4.1.7 is starting: 7491a169dc40e54dfc8923e791c74c633846401dc679ddedddadc9f48e26344c
2019-08-09 17:52:57.105 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 15:52:57.113 [main] INFO org.springframework.core.KotlinDetector - Kotlin reflection implementation not found at runtime, related features won’t be available.
2019-08-09 17:52:57.560 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: _ ____
2019-08-09 17:52:57.560 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: / \ __ _____ _ __ / | ___ _ ____ _____ _ __
2019-08-09 17:52:57.560 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: / _ \ \ / / _ | ’
\
_ \ / _ \ '\ \ / / _ \ '|
2019-08-09 17:52:57.560 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: / ___ \ > < () | | | |) | __/ | \ V / __/ |
2019-08-09 17:52:57.561 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: /
/ _/
/__/|| ||____/ _|| _/ ___||
2019-08-09 17:52:57.561 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: Standard Edition Powered by AxonIQ
2019-08-09 17:52:57.561 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT:
2019-08-09 17:52:57.561 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: version: 4.1.7
2019-08-09 17:52:57.668 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:52:57.678 INFO 7 — [ main] io.axoniq.axonserver.AxonServer : Starting AxonServer on 7491a169dc40 with PID 7 (/opt/axonserver/axonserver.jar started by root in /opt/axonserver)
2019-08-09 17:52:57.669 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:52:57.679 INFO 7 — [ main] io.axoniq.axonserver.AxonServer : No active profile set, falling back to default profiles: default
2019-08-09 17:52:57.956 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:52:57.966 WARN 7 — [kground-preinit] org.springframework.web.HttpLogging : For Jackson Kotlin classes support please add “com.fasterxml.jackson.module:jackson-module-kotlin” to the classpath
2019-08-09 17:53:00.451 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:53:00.463 INFO 7 — [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8024 (http)
2019-08-09 17:53:06.182 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:53:06.197 INFO 7 — [ main] io.axoniq.axonserver.grpc.Gateway : Axon Server Gateway started on port: 8124 - no SSL
2019-08-09 17:53:06.626 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:53:06.641 INFO 7 — [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8024 (http) with context path ‘’
2019-08-09 17:53:06.631 INFO 7200 — [ream-1170173753] docker.axonserver : STDOUT: 2019-08-09 15:53:06.647 INFO 7 — [ main] io.axoniq.axonserver.AxonServer : Started AxonServer in 9.496 seconds (JVM running for 10.035)
2019-08-09 17:53:08.436 INFO 7200 — [ main] :whale: [axoniq/axonserver:4.1.7] : Container axoniq/axonserver:4.1.7 started
2019-08-09 17:53:08.562 INFO 7200 — [ main] c.w.e.ddd.glue.PointRequestStepsDef : Starting PointRequestStepsDef on Mac-mini-van-Frank.local with PID 7200

The configuration to achieve this:

public class ContainerStepsDef {
    private static final String AXON_SERVER_NAME = "axonserver";
    private static final String MY_AXON_SERVER = "my-axon-server";
    private static Logger AXON_SERVER_LOGGER = LoggerFactory.getLogger("docker.axonserver");

    private static final GenericContainer AXON_SERVER = new GenericContainer("axoniq/axonserver:4.1.7")
            .withLabel("name", MY_AXON_SERVER)
            .withLabel("hostname", AXON_SERVER_NAME)
            .withEnv("AXONSERVER_HOSTNAME", AXON_SERVER_NAME)
            .withExposedPorts(8024, 8124)
            .withLogConsumer(new Slf4jLogConsumer(AXON_SERVER_LOGGER)
            );

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        public void initialize(@Nonnull ConfigurableApplicationContext configurableApplicationContext) {
            AXON_SERVER.start();

            Awaitility.waitAtMost(Duration.TEN_SECONDS)
                      .until(AXON_SERVER::isRunning);

            TestPropertyValues.of(
                                  "axoniq.axonserver.name="+ MY_AXON_SERVER,
                                  "axoniq.axonserver.hostname=" + AXON_SERVER_NAME,
                                  "server.port=" + AXON_SERVER.getMappedPort(8024),
                                  "axoniq.axonserver.port=" + AXON_SERVER.getMappedPort(8124)
                                )
                              .applyTo(configurableApplicationContext.getEnvironment());
        }
    }
}

As shown above the server.port will be set to the mapped port and the application is able to find it. However the @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) also uses this property (server.port) for the embedded webserver. Since the axon server port is already bound to this port spring cannot bind the webserver and an exception occurs (Caused by: java.net.BindException: Address already in use).

What is the best way to solve this? I really prefer using the axon server docker image for this purpose.

Regards,

Frank

Hi,

are you sure that the conflict is related to your SprigBootTest WebEnvironment and the Axon container?

As far as I understood it, new GenericContainer("…").withExposedPorts maps the 8024 and 8124 to random ports on your host, which you can read with AXON_SERVER.getMappedPort(…). The random port assignment should be done automatically by the underlying docker run command, which should check for available ports automatically (see https://www.testcontainers.org/features/networking/).
Same applies to the SpringBootTest RANDOM_PORT (see https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications), which should automatically choose a random, available port.

Maybe you could set a breakpoint somewhere before your SpringBootTest.WebEnvironment is started and check your netstat -a for the ports in question.

As far as I can see from your log, you are using MacOS, which adds an additional layer of complexity because docker is run in a virtual enviroment as far as I know.

Hope some of this makes sense and helps to find an error.

Best Regards

Hi Jakob,

Sorry for my late reply, but thanks for your reponse. It turned out I mapped the ports in the wrong direction. The setup underneath works fine: