如何通过测试容器启动 Informix?

How to launch Informix via testcontainers?

我对在我的项目中使用 testcontainers 非常感兴趣。

但是,我很难将它设置为与 Informix 一起工作。

请注意,我可以使用 Docker-for-Mac 启动一个 informix 容器,它将构建并启动。

虽然不确定它是否可以与测试容器一起使用。我希望它会。

这是我目前所拥有的

测试class

package com.example.demo;

import com.github.dockerjava.api.command.CreateContainerCmd;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.HostPortWaitStrategy;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitAllStrategy;

import java.time.Duration;

import static org.junit.Assert.assertEquals;

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

  private static GenericContainer informix;

  @BeforeClass
  public static void init() {
    informix = new GenericContainer("ibmcom/informix-innovator-c")
        .withExposedPorts(9088)
        .withEnv("LICENSE", "accept")
        .withPrivilegedMode(true)
        .withCreateContainerCmdModifier(command -> ((CreateContainerCmd)command).withTty(Boolean.TRUE))
        .waitingFor(new WaitAllStrategy().withStrategy(new LogMessageWaitStrategy().withRegEx(".*listener on port.*\n"))
            .withStrategy(new HostPortWaitStrategy())
            .withStartupTimeout(Duration.ofMinutes(2)));

    informix.start();
  }

  @AfterClass
  public static void destroy(){
    informix.close();
  }

  @Test
  public void testDemo() {
    int foo = 1;
    assertEquals(foo, 1);
  }

}

容器启动然后永远挂起,永远不会进入测试

这是输出:

...
00:32:27  Updating Low Memory Manager to version 11 
00:32:27  Installing patch to Low Memory Manager code. version(11.01)
00:32:27  Installing patch to upgrade ph_task code. version(13.08)
00:32:27  SCHAPI: Started 2 dbWorker threads.
00:32:27  Checkpoint Completed:  duration was 0 seconds.
00:32:27  Sat Jul  6 - loguniq 3, logpos 0xa2e080, timestamp: 0x32abd Interval: 6

00:32:27  Maximum server connections 1 
00:32:27  Checkpoint Statistics - Avg. Txn Block Time 0.000, # Txns blocked 1, Plog used 178, Llog used 4831

starting mongo listener on port 27017
starting rest listener on port 27018
starting mqtt listener on port 27883
17:34:05.472 [main] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: b252c6f234c9d133d17f64f54344061e3689758089339abb7eda3f056adde536,false,com.github.dockerjava.core.exec.InspectContainerCmdExec@3fabf088
17:34:05.472 [main] DEBUG com.github.dockerjava.core.exec.InspectContainerCmdExec - GET: OkHttpWebTarget(okHttpClient=org.testcontainers.shaded.okhttp3.OkHttpClient@70925b45, baseUrl=http://docker.socket/, path=[/containers/b252c6f234c9d133d17f64f54344061e3689758089339abb7eda3f056adde536/json], queryParams={})
17:34:05.482 [main] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: b252c6f234c9d133d17f64f54344061e3689758089339abb7eda3f056adde536,<null>,com.github.dockerjava.core.exec.KillContainerCmdExec@5cbf9e9f
17:34:06.186 [main] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: b252c6f234c9d133d17f64f54344061e3689758089339abb7eda3f056adde536,false,com.github.dockerjava.core.exec.InspectContainerCmdExec@18e8473e
17:34:06.187 [main] DEBUG com.github.dockerjava.core.exec.InspectContainerCmdExec - GET: OkHttpWebTarget(okHttpClient=org.testcontainers.shaded.okhttp3.OkHttpClient@70925b45, baseUrl=http://docker.socket/, path=[/containers/b252c6f234c9d133d17f64f54344061e3689758089339abb7eda3f056adde536/json], queryParams={})
17:34:06.194 [main] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: b252c6f234c9d133d17f64f54344061e3689758089339abb7eda3f056adde536,true,true,com.github.dockerjava.core.exec.RemoveContainerCmdExec@6f6a7463
17:34:06.297 [main] DEBUG org.testcontainers.utility.ResourceReaper - Removed container and associated volume(s): ibmcom/informix-innovator-c:latest
17:34:06.298 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@624ea235 testClass = DemoApplicationTests, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@3932c79a testClass = DemoApplicationTests, locations = '{}', classes = '{class com.example.demo.DemoApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@3e2e18f2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@703580bf, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@5c30a9b0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3c407114], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].

org.testcontainers.containers.ContainerLaunchException: Container startup failed

    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:217)
    at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:199)
    at com.example.demo.DemoApplicationTests.init(DemoApplicationTests.java:37)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.rnorth.ducttape.RetryCountExceededException: Retry limit hit with exception
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:83)
    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:210)
    ... 20 more
Caused by: org.testcontainers.containers.ContainerLaunchException: Could not create/start container
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:277)
    at org.testcontainers.containers.GenericContainer.lambda$doStart[=11=](GenericContainer.java:212)
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:76)
    ... 21 more
Caused by: org.rnorth.ducttape.TimeoutException: java.util.concurrent.TimeoutException
    at org.rnorth.ducttape.timeouts.Timeouts.callFuture(Timeouts.java:70)
    at org.rnorth.ducttape.timeouts.Timeouts.doWithTimeout(Timeouts.java:60)
    at org.testcontainers.containers.wait.strategy.WaitAllStrategy.waitUntilReady(WaitAllStrategy.java:53)
    at org.testcontainers.containers.GenericContainer.waitUntilContainerStarted(GenericContainer.java:582)
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:259)
    ... 23 more
Caused by: java.util.concurrent.TimeoutException
    at java.util.concurrent.FutureTask.get(FutureTask.java:205)
    at org.rnorth.ducttape.timeouts.Timeouts.callFuture(Timeouts.java:65)
    ... 27 more


Disconnected from the target VM, address: '127.0.0.1:51062', transport: 'socket'

Test ignored.

Process finished with exit code 255

如果我注释掉这一行

// .withCreateContainerCmdModifier(命令 -> ((CreateContainerCmd)命令).withTty(Boolean.TRUE))

然后它开始,但随后陷入无休止的循环中,这些消息最终以相同的方式超时

6:09:07.489 [ducttape-1] DEBUG com.github.dockerjava.core.exec.InspectExecCmdExec - GET: OkHttpWebTarget(okHttpClient=org.testcontainers.shaded.okhttp3.OkHttpClient@1a2e2935, baseUrl=http://docker.socket/, path=[/exec/ab3ed2f2d3fa129b21c787a7bcce603a267fea48268ab7c861657005919ee546/json], queryParams={})
16:09:08.494 [ducttape-1] DEBUG org.testcontainers.containers.ExecInContainerPattern - /elastic_raman: Running "exec" command: /bin/sh -c cat /proc/net/tcp{,6} | awk '{print }' | grep -i :2380
16:09:08.495 [ducttape-1] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: 3a30ec6ec83dff4b2f330b9c9d71b907dbfd7596a0bd9cc54d1b4f7eb2164f3e,<null>,true,true,<null>,<null>,<null>,{/bin/sh,-c,cat /proc/net/tcp{,6} | awk '{print }' | grep -i :2380},com.github.dockerjava.core.exec.ExecCreateCmdExec@74c5bfba
16:09:08.582 [ducttape-1] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: b82e102d716c5530e5aa8c9699f8f5a5e0d6aa6cc4aa69c44b8553f79de4101f,com.github.dockerjava.core.exec.InspectExecCmdExec@90283ab
16:09:08.582 [ducttape-1] DEBUG com.github.dockerjava.core.exec.InspectExecCmdExec - GET: OkHttpWebTarget(okHttpClient=org.testcontainers.shaded.okhttp3.OkHttpClient@1a2e2935, baseUrl=http://docker.socket/, path=[/exec/b82e102d716c5530e5aa8c9699f8f5a5e0d6aa6cc4aa69c44b8553f79de4101f/json], queryParams={})

Informix 的 docker 映像中存在错误配置。在 docker 容器中启动的服务器只会监听主机名,而不是本地主机。 Testcontainers 使用 'localhost' 作为网络接口来连接到您的容器。因此,当您使用 .withExposedPorts(9088) 时,该端口实际上并未暴露在 TestContainers 可以连接到的网络接口上。

这就是为什么即使您在等待日志消息时您仍然很可能遇到问题,您也在端口上等待并且它永远不可用。

好消息是这个问题现在已经修复,可以通过下载最新的 Informix docker 图像

ibmcom/informix-developer-database:latest 最新 14.10 docker 图片

下面是我 运行 验证新图像与 TestContainers 一起工作得更好的代码。

public class DockerTest {
    GenericContainer<?>container  = new GenericContainer<>("ibmcom/informix-developer-database:latest")
        .withExposedPorts(9088, 9089, 27017, 27018, 27883).withEnv("LICENSE", "accept");
@Test
public void testIfxContainer() throws Exception {
    container.start();
    System.out.println("Informix started");
    //test the connection
    try(Connection c = DriverManager.getConnection("jdbc:informix-sqli:localhost:"  + container.getFirstMappedPort() + "/sysmaster:user=informix;password=your-password")) {
      try(Statement s = c.createStatement(); ResultSet rs = s.executeQuery("SELECT FIRST 10 tabname from systables");) {
        while(rs.next()) {
          System.out.println(r.getString(1));
        }
      }
    }
  }
}