Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using @ServiceConnection on a Container @Bean method does not work with AOT-processed tests #42851

Open
wyhasany opened this issue Oct 23, 2024 · 7 comments
Labels
theme: aot An issue related to Ahead-of-time processing type: bug A general bug
Milestone

Comments

@wyhasany
Copy link

Spring Boot application fails to run the nativeTest task with Testcontainers.

I generated a simple project using start.spring.io:

image

On the first run, it fails due to a reflection issue:

...
o.t.d.DockerClientProviderStrategy       : Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
	UnixSocketClientProviderStrategy: failed with exception RuntimeException (org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.github.dockerjava.api.model.RuntimeInfo: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.github.dockerjava.api.model.Info["Runtimes"]->java.util.LinkedHashMap["io.containerd.runc.v2"])). Root cause JsonMappingException (Can not construct instance of com.github.dockerjava.api.model.RuntimeInfo: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.github.dockerjava.api.model.Info["Runtimes"]->java.util.LinkedHashMap["io.containerd.runc.v2"]))
	DockerDesktopClientProviderStrategy: failed with exception RuntimeException (org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.github.dockerjava.api.model.RuntimeInfo: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.github.dockerjava.api.model.Info["Runtimes"]->java.util.LinkedHashMap["io.containerd.runc.v2"])). Root cause JsonMappingException (Can not construct instance of com.github.dockerjava.api.model.RuntimeInfo: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.github.dockerjava.api.model.Info["Runtimes"]->java.util.LinkedHashMap["io.containerd.runc.v2"]))As no valid configuration was found, execution cannot continue.
See https://java.testcontainers.org/on_failure.html for more details.
...

I solved this issue by adding a reflect.json file in src/test/resources/META-INF/native-image/ with the following content::

[
  {
    "name":"com.github.dockerjava.api.model.RuntimeInfo",
    "allDeclaredFields":true,
    "queryAllDeclaredMethods":true,
    "queryAllDeclaredConstructors":true,
    "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"setPath","parameterTypes":["java.lang.String"] }]
  },
  {
    "name":"com.github.dockerjava.api.model.Info",
    "allDeclaredFields":true,
    "queryAllDeclaredMethods":true,
    "queryAllDeclaredConstructors":true,
    "methods":[{"name":"<init>","parameterTypes":[] }]
  }
]

However, I'm confused because Testcontainers seems to be supported by GraalVM as indicated here: https://www.graalvm.org/native-image/libraries-and-frameworks/

With that configuration, I encountered another issue that I'm unsure how to resolve:

> Task :nativeTest
JUnit Platform on Native Image - report
----------------------------------------

12:37:58.794 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.example.demo.DemoApplicationTests]: DemoApplicationTests does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
12:37:58.797 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.example.demo.DemoApplication for test class com.example.demo.DemoApplicationTests

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.3.4)

2024-10-23T12:37:58.813+02:00  INFO 9667 --- [demo] [           main] com.example.demo.DemoApplicationTests    : Starting AOT-processed DemoApplicationTests using Java 21.0.4 with PID 9667 (started by rowickim in /private/tmp/demo)
2024-10-23T12:37:58.813+02:00  INFO 9667 --- [demo] [           main] com.example.demo.DemoApplicationTests    : No active profile set, falling back to 1 default profile: "default"
2024-10-23T12:37:58.816+02:00  INFO 9667 --- [demo] [           main] org.testcontainers.images.PullPolicy     : Image pull policy will be performed by: DefaultPullPolicy()
2024-10-23T12:37:58.816+02:00  INFO 9667 --- [demo] [           main] o.t.utility.ImageNameSubstitutor         : Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
2024-10-23T12:37:58.825+02:00  INFO 9667 --- [demo] [           main] o.t.d.DockerClientProviderStrategy       : Loaded org.testcontainers.dockerclient.UnixSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
2024-10-23T12:37:58.846+02:00  INFO 9667 --- [demo] [           main] o.t.d.DockerClientProviderStrategy       : Found Docker environment with local Unix socket (unix:///var/run/docker.sock)
2024-10-23T12:37:58.847+02:00  INFO 9667 --- [demo] [           main] org.testcontainers.DockerClientFactory   : Docker host IP address is localhost
2024-10-23T12:37:58.852+02:00  INFO 9667 --- [demo] [           main] org.testcontainers.DockerClientFactory   : Connected to docker:
  Server Version: 27.2.0
  API Version: 1.47
  Operating System: Docker Desktop
  Total Memory: 7838 MB
2024-10-23T12:37:58.852+02:00  WARN 9667 --- [demo] [           main] o.testcontainers.utility.ResourceReaper  :
********************************************************************************
Ryuk has been disabled. This can cause unexpected behavior in your environment.
********************************************************************************
2024-10-23T12:37:58.852+02:00  INFO 9667 --- [demo] [           main] org.testcontainers.DockerClientFactory   : Checking the system...
2024-10-23T12:37:58.852+02:00  INFO 9667 --- [demo] [           main] org.testcontainers.DockerClientFactory   : ✔︎ Docker server version should be at least 1.6.0
2024-10-23T12:37:58.859+02:00  INFO 9667 --- [demo] [           main] tc.mongo:latest                          : Creating container for image: mongo:latest
2024-10-23T12:37:59.004+02:00  INFO 9667 --- [demo] [           main] tc.mongo:latest                          : Container mongo:latest is starting: cb2dd662645f26c2cb8ffdf5dd73d4332628b60c2a5e6487fcfe101c4480456e
2024-10-23T12:37:59.877+02:00  INFO 9667 --- [demo] [           main] tc.mongo:latest                          : Container mongo:latest started in PT1.018256S
2024-10-23T12:38:00.339+02:00  WARN 9667 --- [demo] [           main] o.s.c.support.GenericApplicationContext  : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'standardMongoSettingsCustomizer': Unsatisfied dependency expressed through method 'standardMongoSettingsCustomizer' parameter 1: No qualifying bean of type 'org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

It looks like the task aotTestClasses doesn't add any MongoConnectionDetails to the application context.

Here is the zipped project for reference: demo.zip

Environment Details

I'm using the following version of GraalVM:

❯ java -version
java version "21.0.4" 2024-07-16 LTS
Java(TM) SE Runtime Environment Oracle GraalVM 21.0.4+8.1 (build 21.0.4+8-LTS-jvmci-23.1-b41)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 21.0.4+8.1 (build 21.0.4+8-LTS-jvmci-23.1-b41, mixed mode, sharing)
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 23, 2024
@wilkinsona
Copy link
Member

wilkinsona commented Oct 23, 2024

This is similar to #35663, although that issue was using @ServiceConnection on a field in a test class whereas this issue's using @ServiceConnection on a @Bean method. I wonder if that difference may be where the problem lies with the fix for #35663 possibly going too far and adversely affecting the @Bean case as well as fixing the field case.

@wyhasany
Copy link
Author

@wilkinsona it works with following configuration:

@SpringBootTest
@Testcontainers
class DemoApplicationTests {

	@Container
	@ServiceConnection
	static MongoDBContainer mongoDbContainer = new MongoDBContainer(DockerImageName.parse("mongo:latest"));

	@Test
	void contextLoads() {
	}

}

@wilkinsona
Copy link
Member

That's good to know, @wyhasany. Thank you. We'll see what we can do for the @Bean approach.

@wilkinsona wilkinsona changed the title Spring Boot + Mongo + Testcontainers + GraalVM: Failing Native Integration Tests Using @ServiceConnection on a Container @Bean method does not work with AOT-processed tests Oct 24, 2024
@wilkinsona wilkinsona added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Oct 24, 2024
@wilkinsona wilkinsona added this to the 3.2.x milestone Oct 24, 2024
@nosan

This comment was marked as off-topic.

@wilkinsona

This comment was marked as off-topic.

@wilkinsona

This comment was marked as off-topic.

@wilkinsona
Copy link
Member

Undoing the fix for #35663 results in the following failure during AOT processing:

Exception in thread "main" org.springframework.test.context.aot.TestContextAotException: Failed to generate AOT artifacts for test classes [com.example.demo.DemoApplicationTests]
        at org.springframework.test.context.aot.TestContextAotGenerator.lambda$processAheadOfTime$5(TestContextAotGenerator.java:286)
        at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
        at org.springframework.util.MultiValueMapAdapter.forEach(MultiValueMapAdapter.java:179)
        at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:244)
        at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:205)
        at org.springframework.test.context.aot.TestAotProcessor.performAotProcessing(TestAotProcessor.java:91)
        at org.springframework.test.context.aot.TestAotProcessor.doProcess(TestAotProcessor.java:72)
        at org.springframework.test.context.aot.TestAotProcessor.doProcess(TestAotProcessor.java:39)
        at org.springframework.context.aot.AbstractAotProcessor.process(AbstractAotProcessor.java:81)
        at org.springframework.boot.test.context.SpringBootTestAotProcessor.main(SpringBootTestAotProcessor.java:63)
Caused by: org.springframework.test.context.aot.TestContextAotException: Failed to process test class [com.example.demo.DemoApplicationTests] for AOT
        at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:323)
        at org.springframework.test.context.aot.TestContextAotGenerator.lambda$processAheadOfTime$5(TestContextAotGenerator.java:277)
        ... 9 more
Caused by: java.lang.IllegalStateException: Error processing bean with name 'mongoContainerConnectionDetailsForMongoDbContainer': instance supplier is not supported
        at org.springframework.beans.factory.aot.DefaultBeanRegistrationCodeFragments.getTarget(DefaultBeanRegistrationCodeFragments.java:83)
        at org.springframework.beans.factory.aot.BeanDefinitionMethodGenerator.generateBeanDefinitionMethod(BeanDefinitionMethodGenerator.java:85)
        at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.lambda$generateRegisterBeanDefinitionsMethod$2(BeanRegistrationsAotContribution.java:90)
        at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721)
        at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.generateRegisterBeanDefinitionsMethod(BeanRegistrationsAotContribution.java:88)
        at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.lambda$applyTo$1(BeanRegistrationsAotContribution.java:73)
        at org.springframework.aot.generate.GeneratedMethod.<init>(GeneratedMethod.java:54)
        at org.springframework.aot.generate.GeneratedMethods.add(GeneratedMethods.java:112)
        at org.springframework.aot.generate.GeneratedMethods.add(GeneratedMethods.java:89)
        at org.springframework.beans.factory.aot.BeanRegistrationsAotContribution.applyTo(BeanRegistrationsAotContribution.java:72)
        at org.springframework.context.aot.BeanFactoryInitializationAotContributions.applyTo(BeanFactoryInitializationAotContributions.java:78)
        at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$processAheadOfTime$0(ApplicationContextAotGenerator.java:58)
        at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67)
        at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53)
        at org.springframework.test.context.aot.TestContextAotGenerator.processAheadOfTime(TestContextAotGenerator.java:319)
        ... 10 more

This is caused by ConnectionDetailsRegistrar defining a bean that uses an instance supplier:

Unlike #35663, where there is a test context customizer that will register the beans when the tests are being run, skipping these beans results in them not being defined at all. It looks like we need to find a way to register them during AOT processing without using an instance supplier.

@wilkinsona wilkinsona added the theme: aot An issue related to Ahead-of-time processing label Oct 24, 2024
@philwebb philwebb modified the milestones: 3.2.x, 3.3.x Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-of-time processing type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants