-
Notifications
You must be signed in to change notification settings - Fork 40.9k
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
DynamicPropertyRegistry Values Not Set After Dev Tools Reload for RestartScope Containers #41552
Comments
I did some research regarding this bug, and frankly, it is quite a tricky one. First run: TestcontainersLifecycleBeanPostProcessor
-- initializeContainers()
RestartScopeInitializer.RestartScope
-- Restarter.getOrAddAttribute('redisContainer', ObjectFactory<?> factory)
-- 'redisContainer' does not exist in attributes, so ObjectFactory<?> is being called.
TestcontainersPropertySourceAutoConfiguration.dynamicPropertyRegistry() is being called because it is needed for 'redisContainer'
-- Attach 'testcontainersPropertySource'
-- Register bean definition named: EventPublisherRegistrar.class.getName()
TestcontainersConfiguration.redisContainer()
-- create RedisContainer
-- Set RedisContainer properties to the 'dynamicPropertyRegistry'
TestcontainersLifecycleBeanPostProcessor.initializeStartables()
-- Start containers
DemoController @Value annotations are being processed. Restart: TestcontainersLifecycleBeanPostProcessor
-- initializeContainers()
RestartScopeInitializer.RestartScope
-- Restarter.getOrAddAttribute("redisContainer", ObjectFactory<?>)
-- "redisContainer" exists in attributes, so ObjectFactory<?> **is not being called.**
DemoController @Value annotations are not being processed due to Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'MY_REDIS_HOST' in value "${MY_REDIS_HOST}" There are two potential issues with this bug. The first one The second one is that methods annotated A potential fix is:
This branch contains an implementation of the fix that I have tried to describe above. I used this example for research: https://github.com/shawnweeks/spring_boot_testcontainers_restartscope_issue |
Thanks for the detailed analysis @nosan. This is indeed a complicated issue to fix and made even more difficult by the changes we're introducing in 3.4 due to #41996. I'm not really sure if we should attempt to fix this in 3.3 since I don't really like the fact that The following code does appear to work with 3.4.0-SNAPSHOT: @TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
@RestartScope
public GenericContainer<?> redisContainer() {
GenericContainer<?> redisContainer = new GenericContainer<>("redis:7");
redisContainer.withExposedPorts(6379);
redisContainer.withCommand("redis-server", "--requirepass redis_user", "--save 60 1", "--loglevel debug");
return redisContainer;
}
@Bean
public DynamicPropertyRegistrar redisContainerProperties(GenericContainer<?> redisContainer) {
return (registry) -> {
registry.add("MY_REDIS_HOST", () -> "localhost");
registry.add("MY_REDIS_PORT", () -> redisContainer.getMappedPort(6379));
registry.add("MY_REDIS_PASSWORD", () -> "redis_user");
};
}
} This split allows the container to remain in the |
The following ugly hack will work with 3.3.x: package com.example.demo;
import java.util.List;
import org.springframework.boot.devtools.restart.RestartScope;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.lifecycle.Startable;
import com.github.dockerjava.api.command.InspectContainerResponse;
@TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
@RestartScope
public GenericContainer<?> redisContainer(DynamicPropertyRegistry registry) {
GenericContainer<?> redisContainer = new GenericContainer<>("redis:7");
redisContainer.withExposedPorts(6379);
redisContainer.withCommand("redis-server", "--requirepass redis_user", "--save 60 1", "--loglevel debug");
return redisContainer;
}
@Bean
public RedisProperties redisProperties(GenericContainer<?> redisContainer, DynamicPropertyRegistry registry) {
redisContainer.start();
registry.add("MY_REDIS_HOST", () -> "localhost");
registry.add("MY_REDIS_PORT", () -> redisContainer.getMappedPort(6379));
registry.add("MY_REDIS_PASSWORD", () -> "redis_user");
return new RedisProperties();
}
static class RedisProperties implements Startable, ContainerState {
@Override
public void start() {
}
@Override
public void stop() {
}
@Override
public List<Integer> getExposedPorts() {
return null;
}
@Override
public InspectContainerResponse getContainerInfo() {
return null;
}
}
} |
Flagging to see if the team think we should still attempt a fix in 3.2/3.3. |
I propose adding a note to the documentation explaining that Fixing this issue in versions 3.2/3.3 would result in inconsistency with 3.4.x, as the latter will not receive a similar fix. |
This hack works with 3.2.x, 3.3.x and 3.4.0-M3 @TestConfiguration(proxyBeanMethods = false)
class TestcontainersConfiguration {
@Bean
@RestartScope
public GenericContainer<?> redisContainer() {
GenericContainer<?> redisContainer = new GenericContainer<>("redis:7");
redisContainer.withExposedPorts(6379);
redisContainer.withCommand("redis-server", "--requirepass redis_user", "--save 60 1", "--loglevel debug");
return redisContainer;
}
@Autowired
void registryRedisContainerProperties(@Lazy GenericContainer<?> redisContainer, DynamicPropertyRegistry registry) {
registry.add("MY_REDIS_HOST", () -> "localhost");
registry.add("MY_REDIS_PORT", () -> redisContainer.getMappedPort(6379));
registry.add("MY_REDIS_PASSWORD", () -> "redis_user");
}
} |
It'll fail by default with 3.4.0-SNAPSHOT due to the deprecation of support for injecting |
We're going to leave this one open to see if we can fix it in some way, but it's hard to see us getting to it quickly. Upgrading to 3.4. when it's out is probably the best option for anyone with this issue. |
I've updated to 3.4.1 and am using the new DynamicPropertyRegistrar however there are still issues namely the DynamicPropertyRegistrar bean is created early in the startup process however the initialize method is called too late in the process for all the placeholders in my data sources to resolve. On 3.3.x I could use a bean post processor on the test side to force a dependency between my data source classes and dynamicPropertyRegistry but this no longer works. |
@shawnweeks can you please update https://github.com/shawnweeks/spring_boot_testcontainers_restartscope_issue along those lines so that we can take a look? |
I'm probably misunderstanding why the issue happens but here is an example, sorry it's a little convoluted but it's based on a real application with the same issue. See https://github.com/shawnweeks/spring_boot_testcontainers_restartscope_issue/tree/issue_v2 |
@shawnweeks I think the issue in the sample is the You can use a @Bean
DelegatingFilterProxyRegistrationBean demoFilterRegistration() {
return new DelegatingFilterProxyRegistrationBean("demoFilter");
} |
I've updated the example, in the real program the filters are for Spring Security and it appears that what you suggest works when used by it self but if your using |
If I change to something like this everything appears to work the new way.
|
When using a combination of
@RestartScope
Test Containers and theDynamicPropertyRegistry
registry property values are not set back after Spring Dev Tools reload the project. The issue was partially resolved in #35786 but that fix does not appear to resolve the entire issue. I've linked to a small example project demonstrating the issue. See https://github.com/shawnweeks/spring_boot_testcontainers_restartscope_issueThe text was updated successfully, but these errors were encountered: