From 851806c9297875b779eb81a7896431fa5651fabb Mon Sep 17 00:00:00 2001 From: j-sandy <30489233+j-sandy@users.noreply.github.com> Date: Thu, 15 Feb 2024 18:16:47 +0530 Subject: [PATCH] fix(tests): fix enableRedisKeyspaceNotificationsInitializer bean creation issue during upgrade to spring boot 2.6.x While upgrading spring boot 2.6.15 and spring cloud 2021.0.8, encounter below errors during execution of tests under gate-api-tck and gate-plugins-test modules: ``` Failed to load ApplicationContext java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124) at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190) at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248) at com.netflix.spinnaker.gate.plugins.test.GatePluginsTest$tests$1$1$invoke$$inlined$serviceFixture$1.invoke(PluginsTck.kt:78) at com.netflix.spinnaker.gate.plugins.test.GatePluginsTest$tests$1$1$invoke$$inlined$serviceFixture$1.invoke(PluginsTck.kt:76) ... Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [com/netflix/spinnaker/gate/config/GateConfig.class]: Invocation of init method failed; nested exception is org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) at app//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) at app//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) at app//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) at app//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) at app//org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920) at app//org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) at app//org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745) at app//org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:423) at app//org.springframework.boot.SpringApplication.run(SpringApplication.java:307) at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:148) at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141) at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90) ... 171 more Caused by: org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at app//org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:292) at app//org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:514) at app//org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration$EnableRedisKeyspaceNotificationsInitializer.afterPropertiesSet(RedisHttpSessionConfiguration.java:331) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863) at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800) ... 186 more Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool at app//redis.clients.jedis.util.Pool.getResource(Pool.java:84) at app//redis.clients.jedis.JedisPool.getResource(JedisPool.java:370) at app//redis.clients.jedis.JedisPool.getResource(JedisPool.java:15) at app//org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:283) ... 190 more Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Failed to create socket. at app//redis.clients.jedis.DefaultJedisSocketFactory.createSocket(DefaultJedisSocketFactory.java:110) at app//redis.clients.jedis.Connection.connect(Connection.java:226) at app//redis.clients.jedis.BinaryClient.connect(BinaryClient.java:140) at app//redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:310) at app//redis.clients.jedis.BinaryJedis.initializeFromClientConfig(BinaryJedis.java:88) at app//redis.clients.jedis.BinaryJedis.(BinaryJedis.java:293) at app//redis.clients.jedis.Jedis.(Jedis.java:169) at app//redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:177) at app//org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:571) at app//org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:298) at app//org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:223) at app//redis.clients.jedis.util.Pool.getResource(Pool.java:75) ... 193 more Caused by: java.net.ConnectException: Connection refused at java.base/sun.nio.ch.Net.pollConnect(Native Method) at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:547) at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:602) at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) at java.base/java.net.Socket.connect(Socket.java:633) at redis.clients.jedis.DefaultJedisSocketFactory.createSocket(DefaultJedisSocketFactory.java:80) ... 204 more ``` In order to fix this issue add `@TestConfiguration` to create the required beans for initialization of `enableRedisKeyspaceNotificationsInitializer` bean. --- gate-api-tck/gate-api-tck.gradle | 2 ++ .../spinnaker/gate/api/test/GateFixture.kt | 31 +++++++++++++++++++ gate-plugins-test/gate-plugins-test.gradle | 2 ++ .../gate/plugins/test/GatePluginsFixture.kt | 27 +++++++++++++++- 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/gate-api-tck/gate-api-tck.gradle b/gate-api-tck/gate-api-tck.gradle index 35e8d220a0..7dd6a08c93 100644 --- a/gate-api-tck/gate-api-tck.gradle +++ b/gate-api-tck/gate-api-tck.gradle @@ -3,6 +3,8 @@ apply from: "${project.rootDir}/gradle/kotlin-test.gradle" dependencies { implementation(project(":gate-web")) + implementation("io.spinnaker.kork:kork-jedis-test") + implementation("org.springframework.session:spring-session-data-redis") api("org.springframework.boot:spring-boot-starter-test") api("dev.minutest:minutest") diff --git a/gate-api-tck/src/main/kotlin/com/netflix/spinnaker/gate/api/test/GateFixture.kt b/gate-api-tck/src/main/kotlin/com/netflix/spinnaker/gate/api/test/GateFixture.kt index bdbeff87d7..6a2b81eee9 100644 --- a/gate-api-tck/src/main/kotlin/com/netflix/spinnaker/gate/api/test/GateFixture.kt +++ b/gate-api-tck/src/main/kotlin/com/netflix/spinnaker/gate/api/test/GateFixture.kt @@ -20,10 +20,20 @@ import com.netflix.spinnaker.gate.Main import dev.minutest.TestContextBuilder import dev.minutest.TestDescriptor import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.context.TestConfiguration import org.springframework.test.context.TestContextManager import org.springframework.test.context.TestPropertySource +import org.springframework.context.annotation.Bean +import com.netflix.spinnaker.kork.jedis.EmbeddedRedis +import org.springframework.context.annotation.Primary +import org.springframework.data.redis.connection.RedisStandaloneConfiguration +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory +import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory +import org.springframework.test.context.ContextConfiguration +import redis.clients.jedis.JedisPool @SpringBootTest(classes = [Main::class]) +@ContextConfiguration(classes = [GateFixtureConfiguration::class]) @TestPropertySource(properties = ["spring.config.location=classpath:gate-test-app.yml"]) abstract class GateFixture @@ -39,3 +49,24 @@ inline fun TestContextBuilder.gateFixture( } } } + +@TestConfiguration +internal open class GateFixtureConfiguration { + @Bean(destroyMethod = "destroy") + fun embeddedRedis(): EmbeddedRedis { + return EmbeddedRedis.embed().also { redis -> redis.jedis.connect() }.also { redis -> redis.jedis.ping() } + } + + @Bean + @Primary + @SpringSessionRedisConnectionFactory + fun jedisConnectionFactory(embeddedRedis: EmbeddedRedis): JedisConnectionFactory { + return JedisConnectionFactory(RedisStandaloneConfiguration(embeddedRedis.host, embeddedRedis.port)) + } + + @Bean + @Primary + fun jedis(embeddedRedis: EmbeddedRedis): JedisPool { + return embeddedRedis.getPool(); + } +} diff --git a/gate-plugins-test/gate-plugins-test.gradle b/gate-plugins-test/gate-plugins-test.gradle index 2c3bf31579..3c4f990212 100644 --- a/gate-plugins-test/gate-plugins-test.gradle +++ b/gate-plugins-test/gate-plugins-test.gradle @@ -9,6 +9,8 @@ dependencies { testImplementation("io.spinnaker.kork:kork-plugins") testImplementation("io.spinnaker.kork:kork-plugins-tck") + testImplementation("io.spinnaker.kork:kork-jedis-test") + testImplementation("org.springframework.session:spring-session-data-redis") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") } diff --git a/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt b/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt index af93403da2..a709c94aca 100644 --- a/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt +++ b/gate-plugins-test/src/test/kotlin/com/netflix/spinnaker/gate/plugins/test/GatePluginsFixture.kt @@ -30,6 +30,13 @@ import org.springframework.boot.test.context.TestConfiguration import org.springframework.test.context.ContextConfiguration import org.springframework.test.context.TestPropertySource import org.springframework.test.web.servlet.MockMvc +import org.springframework.context.annotation.Bean +import com.netflix.spinnaker.kork.jedis.EmbeddedRedis +import org.springframework.context.annotation.Primary +import org.springframework.data.redis.connection.RedisStandaloneConfiguration +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory +import org.springframework.session.data.redis.config.annotation.SpringSessionRedisConnectionFactory +import redis.clients.jedis.JedisPool class GatePluginsFixture : PluginsTckFixture, GateTestService() { @@ -75,4 +82,22 @@ class GatePluginsFixture : PluginsTckFixture, GateTestService() { abstract class GateTestService @TestConfiguration -internal open class PluginTestConfiguration +internal open class PluginTestConfiguration { + @Bean(destroyMethod = "destroy") + fun embeddedRedis(): EmbeddedRedis { + return EmbeddedRedis.embed().also { redis -> redis.jedis.connect() }.also { redis -> redis.jedis.ping() } + } + + @Bean + @Primary + @SpringSessionRedisConnectionFactory + fun jedisConnectionFactory(embeddedRedis: EmbeddedRedis): JedisConnectionFactory { + return JedisConnectionFactory(RedisStandaloneConfiguration(embeddedRedis.host, embeddedRedis.port)) + } + + @Bean + @Primary + fun jedis(embeddedRedis: EmbeddedRedis): JedisPool { + return embeddedRedis.getPool(); + } +}