diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java index 886e9e7b2f..95cf263616 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigUtils.java @@ -339,10 +339,11 @@ public static void registerSingle(ConfigurableBootstrapContext bootstrapCont String name, ApplicationListener listener) { bootstrapContext.registerIfAbsent(cls, BootstrapRegistry.InstanceSupplier.of(instance)); bootstrapContext.addCloseListener(event -> { - if (event.getApplicationContext().getBeanFactory().getSingleton(name) == null) { - event.getApplicationContext() - .getBeanFactory() - .registerSingleton(name, event.getBootstrapContext().get(cls)); + + T singleton = event.getBootstrapContext().get(cls); + + if (event.getApplicationContext().getBeanFactory().getSingleton(name) == null && singleton != null) { + event.getApplicationContext().getBeanFactory().registerSingleton(name, singleton); event.getApplicationContext().addApplicationListener(listener); } }); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolver.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolver.java index 95907843ad..51acafc280 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolver.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/KubernetesConfigDataLocationResolver.java @@ -52,6 +52,8 @@ public abstract class KubernetesConfigDataLocationResolver implements ConfigDataLocationResolver, Ordered { + private static final Class PROPERTIES_CLASS = KubernetesClientProperties.class; + private static final boolean RETRY_IS_PRESENT = isPresent("org.springframework.retry.annotation.Retryable", null); private final Log log; @@ -139,8 +141,7 @@ private void registerProperties(ConfigDataLocationResolverContext resolverContex SecretsConfigProperties secretsProperties) { ConfigurableBootstrapContext bootstrapContext = resolverContext.getBootstrapContext(); - registerSingle(bootstrapContext, KubernetesClientProperties.class, clientProperties, - "configDataKubernetesClientProperties"); + registerSingle(bootstrapContext, PROPERTIES_CLASS, clientProperties, "configDataKubernetesClientProperties"); if (configMapProperties != null) { registerSingle(bootstrapContext, ConfigMapConfigProperties.class, configMapProperties, @@ -174,15 +175,14 @@ private static PropertyHolder of(ConfigDataLocationResolverContext context) { private static KubernetesClientProperties clientProperties(ConfigDataLocationResolverContext context, String namespace) { KubernetesClientProperties kubernetesClientProperties; + ConfigurableBootstrapContext bootstrapContext = context.getBootstrapContext(); - if (context.getBootstrapContext().isRegistered(KubernetesClientProperties.class)) { - kubernetesClientProperties = context.getBootstrapContext() - .get(KubernetesClientProperties.class) - .withNamespace(namespace); + if (bootstrapContext.isRegistered(PROPERTIES_CLASS) && bootstrapContext.get(PROPERTIES_CLASS) != null) { + kubernetesClientProperties = bootstrapContext.get(PROPERTIES_CLASS).withNamespace(namespace); } else { kubernetesClientProperties = context.getBinder() - .bindOrCreate(KubernetesClientProperties.PREFIX, Bindable.of(KubernetesClientProperties.class)) + .bindOrCreate(KubernetesClientProperties.PREFIX, Bindable.of(PROPERTIES_CLASS)) .withNamespace(namespace); } diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/DummyConfigDataLocationResolver.java b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/DummyConfigDataLocationResolver.java new file mode 100644 index 0000000000..42e8552270 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/DummyConfigDataLocationResolver.java @@ -0,0 +1,43 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.config.ConfigDataLocation; +import org.springframework.boot.context.config.ConfigDataLocationResolverContext; +import org.springframework.boot.context.config.Profiles; +import org.springframework.boot.logging.DeferredLogFactory; +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.KubernetesConfigDataLocationResolver; + +/** + * @author wind57 + */ +@ConditionalOnProperty(value = "dummy.config.loader.enabled", havingValue = "true", matchIfMissing = false) +class DummyConfigDataLocationResolver extends KubernetesConfigDataLocationResolver { + + DummyConfigDataLocationResolver(DeferredLogFactory factory) { + super(factory); + } + + @Override + protected void registerBeans(ConfigDataLocationResolverContext resolverContext, ConfigDataLocation location, + Profiles profiles, PropertyHolder propertyHolder, KubernetesNamespaceProvider namespaceProvider) { + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ConfigServerTest.java b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ConfigServerTest.java new file mode 100644 index 0000000000..685cb443fb --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ConfigServerTest.java @@ -0,0 +1,78 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.discovery; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +/** + * Test that proves that this + * issue + * is fixed. + * + * @author wind57 + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { "spring.main.cloud-platform=KUBERNETES", + "spring.config.import=kubernetes:, optional:configserver:", "dummy.config.loader.enabled=true" }) +class Fabric8ConfigServerTest { + + private static WireMockServer wireMockServer; + + @Autowired + private ApplicationContext applicationContext; + + @BeforeAll + static void beforeAll() { + wireMockServer = new WireMockServer(options().port(8888)); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + } + + @AfterAll + static void after() { + WireMock.shutdownServer(); + wireMockServer.stop(); + } + + @Test + void test() { + stubFor(get(urlEqualTo("/application/default")).willReturn(aResponse().withStatus(200).withBody("{}"))); + Assertions.assertThat(applicationContext).isNotNull(); + } + + @SpringBootApplication + protected static class TestConfig { + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-fabric8-discovery/src/test/resources/META-INF/spring.factories new file mode 100644 index 0000000000..d92d10c6d0 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-discovery/src/test/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.context.config.ConfigDataLocationResolver=\ +org.springframework.cloud.kubernetes.DummyConfigDataLocationResolver