diff --git a/spring-cloud-kubernetes-client-config/pom.xml b/spring-cloud-kubernetes-client-config/pom.xml index ae492891..e243450a 100644 --- a/spring-cloud-kubernetes-client-config/pom.xml +++ b/spring-cloud-kubernetes-client-config/pom.xml @@ -57,6 +57,16 @@ org.springframework.cloud spring-cloud-starter-bootstrap + + org.springframework.retry + spring-retry + true + + + org.springframework.boot + spring-boot-starter-aop + true + org.springframework.boot @@ -93,12 +103,6 @@ spring-boot-starter-webflux test - - - org.springframework.retry - spring-retry - test - org.springframework.boot spring-boot-starter-aop diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java index 564515a5..93018d0d 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientBootstrapConfiguration.java @@ -27,6 +27,8 @@ import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabl import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesSecretsEnabled; import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesConfigRetryDisabled; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesSecretsRetryDisabled; import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; @@ -46,6 +48,7 @@ public class KubernetesClientBootstrapConfiguration { @Bean @ConditionalOnKubernetesConfigEnabled + @ConditionalOnKubernetesConfigRetryDisabled public KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator( ConfigMapConfigProperties properties, CoreV1Api coreV1Api, KubernetesNamespaceProvider kubernetesNamespaceProvider) { @@ -54,6 +57,7 @@ public class KubernetesClientBootstrapConfiguration { @Bean @ConditionalOnKubernetesSecretsEnabled + @ConditionalOnKubernetesSecretsRetryDisabled public KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties, CoreV1Api coreV1Api, KubernetesNamespaceProvider kubernetesNamespaceProvider) { return new KubernetesClientSecretsPropertySourceLocator(coreV1Api, kubernetesNamespaceProvider, properties); diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientRetryBootstrapConfiguration.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientRetryBootstrapConfiguration.java new file mode 100644 index 00000000..0dff4e67 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientRetryBootstrapConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013-2022 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.client.config; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; +import org.springframework.boot.cloud.CloudPlatform; +import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled; +import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesConfigOrSecretsRetryEnabled; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesConfigRetryEnabled; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesSecretsRetryEnabled; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * @author Ryan Baxter + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnKubernetesEnabled +@AutoConfigureAfter(KubernetesBootstrapConfiguration.class) +@AutoConfigureBefore(KubernetesClientBootstrapConfiguration.class) +@Import({ KubernetesCommonsAutoConfiguration.class, KubernetesClientAutoConfiguration.class }) +@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) +@ConditionalOnKubernetesConfigOrSecretsRetryEnabled +public class KubernetesClientRetryBootstrapConfiguration { + + @Bean + @ConditionalOnKubernetesConfigRetryEnabled + public KubernetesClientConfigMapPropertySourceLocator retryableConfigMapPropertySourceLocator( + ConfigMapConfigProperties properties, CoreV1Api coreV1Api, + KubernetesNamespaceProvider kubernetesNamespaceProvider) { + return new RetryableKubernetesClientConfigMapPropertySourceLocator(coreV1Api, properties, + kubernetesNamespaceProvider); + } + + @Bean + @ConditionalOnKubernetesSecretsRetryEnabled + public KubernetesClientSecretsPropertySourceLocator retryableSecretsPropertySourceLocator( + SecretsConfigProperties properties, CoreV1Api coreV1Api, + KubernetesNamespaceProvider kubernetesNamespaceProvider) { + return new RetryableKubernetesClientSecretsPropertySourceLocator(coreV1Api, kubernetesNamespaceProvider, + properties); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java new file mode 100644 index 00000000..1dc602ed --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientConfigMapPropertySourceLocator.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2020 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.client.config; + +import java.util.Collection; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.retry.annotation.Retryable; + +/** + * ConfigMapPropertySourceLocator for when retry is enabled. + * + * @author Ryan Baxter + */ +class RetryableKubernetesClientConfigMapPropertySourceLocator extends KubernetesClientConfigMapPropertySourceLocator { + + RetryableKubernetesClientConfigMapPropertySourceLocator(CoreV1Api coreV1Api, ConfigMapConfigProperties properties, + KubernetesNamespaceProvider kubernetesNamespaceProvider) { + super(coreV1Api, properties, kubernetesNamespaceProvider); + } + + @Override + @Retryable(interceptor = "kubernetesConfigRetryInterceptor") + public PropertySource locate(Environment environment) { + return super.locate(environment); + } + + @Override + @Retryable(interceptor = "kubernetesConfigRetryInterceptor") + public Collection> locateCollection(Environment environment) { + return super.locateCollection(environment); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java new file mode 100644 index 00000000..ab922d66 --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/RetryableKubernetesClientSecretsPropertySourceLocator.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2022 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.client.config; + +import java.util.Collection; + +import io.kubernetes.client.openapi.apis.CoreV1Api; + +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.retry.annotation.Retryable; + +/** + * SecretsPropertySourceLocator for when retry is enabled. + * + * @author Ryan Baxter + */ +class RetryableKubernetesClientSecretsPropertySourceLocator extends KubernetesClientSecretsPropertySourceLocator { + + RetryableKubernetesClientSecretsPropertySourceLocator(CoreV1Api coreV1Api, + KubernetesNamespaceProvider kubernetesNamespaceProvider, SecretsConfigProperties secretsConfigProperties) { + super(coreV1Api, kubernetesNamespaceProvider, secretsConfigProperties); + } + + @Override + @Retryable(interceptor = "kubernetesSecretsRetryInterceptor") + public PropertySource locate(Environment environment) { + return super.locate(environment); + } + + @Override + @Retryable(interceptor = "kubernetesSecretsRetryInterceptor") + public Collection> locateCollection(Environment environment) { + return super.locateCollection(environment); + } + +} diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java index 5db77916..9d03ae75 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java @@ -49,9 +49,9 @@ public class KubernetesClientEventBasedConfigMapChangeDetector extends Configura private CoreV1Api coreV1Api = null; - private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator; + private final KubernetesClientConfigMapPropertySourceLocator propertySourceLocator; - private SharedInformerFactory factory; + private final SharedInformerFactory factory; private KubernetesClientProperties kubernetesClientProperties; diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java index d62b208e..e81ff395 100644 --- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java +++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java @@ -48,9 +48,9 @@ public class KubernetesClientEventBasedSecretsChangeDetector extends Configurati private CoreV1Api coreV1Api; - private KubernetesClientSecretsPropertySourceLocator propertySourceLocator; + private final KubernetesClientSecretsPropertySourceLocator propertySourceLocator; - private SharedInformerFactory factory; + private final SharedInformerFactory factory; private KubernetesClientProperties kubernetesClientProperties; diff --git a/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories index 067e1fb4..2efd0550 100644 --- a/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-client-config/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientConfigReloadAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -org.springframework.cloud.kubernetes.client.config.KubernetesClientBootstrapConfiguration +org.springframework.cloud.kubernetes.client.config.KubernetesClientBootstrapConfiguration,\ +org.springframework.cloud.kubernetes.client.config.KubernetesClientRetryBootstrapConfiguration diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/EnableRetryBootstrapConfiguration.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/EnableRetryBootstrapConfiguration.java new file mode 100644 index 00000000..c9a5cc5f --- /dev/null +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/EnableRetryBootstrapConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2022 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.client.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.retry.annotation.EnableRetry; + +/** + * @author Ryan Baxter + */ +@Order(0) +@Configuration +@ConditionalOnProperty("spring.cloud.kubernetes.test.enable-retry") +@EnableRetry +public class EnableRetryBootstrapConfiguration { + +} diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorRetryTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorRetryTests.java index 31aee7ce..4a792317 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorRetryTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigMapPropertySourceLocatorRetryTests.java @@ -16,7 +16,10 @@ package org.springframework.cloud.kubernetes.client.config; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.github.tomakehurst.wiremock.WireMockServer; @@ -36,13 +39,26 @@ import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientConfigReloadAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.PropertySource; import org.springframework.mock.env.MockEnvironment; +import org.springframework.retry.annotation.RetryConfiguration; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; @@ -274,6 +290,44 @@ public class KubernetesClientConfigMapPropertySourceLocatorRetryTests { } + @Nested + public class EnableRetryWithoutFailFastTest { + + private ConfigurableApplicationContext context; + + protected void setup(String... env) { + List envList = (env != null) ? new ArrayList<>(Arrays.asList(env)) : new ArrayList<>(); + envList.add("spring.cloud.kubernetes.client.namespace=default"); + String[] envArray = envList.toArray(new String[0]); + + context = new SpringApplicationBuilder(RetryConfiguration.class, PropertyPlaceholderAutoConfiguration.class, + ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class, + EndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, + RefreshEndpointAutoConfiguration.class, ConfigurationPropertiesBindingPostProcessor.class, + ConfigurationPropertiesRebinderAutoConfiguration.class, + KubernetesClientBootstrapConfiguration.class, KubernetesClientRetryBootstrapConfiguration.class, + KubernetesBootstrapConfiguration.class, KubernetesClientConfigReloadAutoConfiguration.class) + .web(org.springframework.boot.WebApplicationType.NONE).properties(envArray).run(); + } + + @AfterEach + public void afterEach() { + if (this.context != null) { + this.context.close(); + this.context = null; + } + } + + @Test + public void doesNotContainRetryableConfigMapPropertySourceLocator() throws Exception { + stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); + setup("debug=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.test.enable-retry=true"); + assertThat(context.containsBean("retryableConfigMapPropertySourceLocator")).isFalse(); + } + + } + @SpringBootApplication static class App { diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorRetryTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorRetryTests.java index a7863e22..02629b96 100644 --- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorRetryTests.java +++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientSecretsPropertySourceLocatorRetryTests.java @@ -16,7 +16,10 @@ package org.springframework.cloud.kubernetes.client.config; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.github.tomakehurst.wiremock.WireMockServer; @@ -36,13 +39,26 @@ import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientConfigReloadAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.PropertySource; import org.springframework.mock.env.MockEnvironment; +import org.springframework.retry.annotation.RetryConfiguration; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; @@ -276,6 +292,45 @@ public class KubernetesClientSecretsPropertySourceLocatorRetryTests { } + @Nested + public class EnableRetryWithoutFailFastTest { + + private ConfigurableApplicationContext context; + + protected void setup(String... env) { + List envList = (env != null) ? new ArrayList<>(Arrays.asList(env)) : new ArrayList<>(); + envList.add("spring.cloud.kubernetes.client.namespace=default"); + String[] envArray = envList.toArray(new String[0]); + + context = new SpringApplicationBuilder(RetryConfiguration.class, PropertyPlaceholderAutoConfiguration.class, + ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class, + EndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, + RefreshEndpointAutoConfiguration.class, ConfigurationPropertiesBindingPostProcessor.class, + ConfigurationPropertiesRebinderAutoConfiguration.class, + KubernetesClientBootstrapConfiguration.class, KubernetesClientRetryBootstrapConfiguration.class, + KubernetesBootstrapConfiguration.class, KubernetesClientConfigReloadAutoConfiguration.class) + .web(org.springframework.boot.WebApplicationType.NONE).properties(envArray).run(); + } + + @AfterEach + public void afterEach() { + if (this.context != null) { + this.context.close(); + this.context = null; + } + } + + @Test + public void doesNotContainRetryableSecretsPropertySourceLocator() throws Exception { + stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error"))); + setup("debug=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.test.enable-retry=true", "spring.cloud.kubernetes.secrets.name=my-secret", + "spring.cloud.kubernetes.secrets.enable-api=true"); + assertThat(context.containsBean("retryableSecretsPropertySourceLocator")).isFalse(); + } + + } + @SpringBootApplication static class App { diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories index 5d0b2c08..6332427d 100644 --- a/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.kubernetes.client.config.boostrap.stubs.IncludeProfileSpecificSourcesConfigurationStub, \ -org.springframework.cloud.kubernetes.client.config.boostrap.stubs.ConfigMapNameAsPrefixConfigurationStub +org.springframework.cloud.kubernetes.client.config.boostrap.stubs.ConfigMapNameAsPrefixConfigurationStub, \ +org.springframework.cloud.kubernetes.client.config.EnableRetryBootstrapConfiguration diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java index 3f54243a..38d96efd 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapPropertySourceLocator.java @@ -37,7 +37,6 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; -import org.springframework.retry.annotation.Retryable; import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName; import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES; @@ -65,7 +64,6 @@ public abstract class ConfigMapPropertySourceLocator implements PropertySourceLo String configurationTarget, ConfigurableEnvironment environment); @Override - @Retryable(interceptor = "kubernetesConfigRetryInterceptor") public PropertySource locate(Environment environment) { if (environment instanceof ConfigurableEnvironment) { ConfigurableEnvironment env = (ConfigurableEnvironment) environment; @@ -85,7 +83,6 @@ public abstract class ConfigMapPropertySourceLocator implements PropertySourceLo } @Override - @Retryable(interceptor = "kubernetesConfigRetryInterceptor") public Collection> locateCollection(Environment environment) { return PropertySourceLocator.super.locateCollection(environment); } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index ee07aca3..7afa8689 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -43,7 +43,6 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.PropertySource; -import org.springframework.retry.annotation.Retryable; /** * Kubernetes {@link PropertySourceLocator} for secrets. @@ -64,7 +63,6 @@ public abstract class SecretsPropertySourceLocator implements PropertySourceLoca } @Override - @Retryable(interceptor = "kubernetesSecretsRetryInterceptor") public PropertySource locate(Environment environment) { if (environment instanceof ConfigurableEnvironment) { ConfigurableEnvironment env = (ConfigurableEnvironment) environment; @@ -86,7 +84,6 @@ public abstract class SecretsPropertySourceLocator implements PropertySourceLoca } @Override - @Retryable(interceptor = "kubernetesSecretsRetryInterceptor") public Collection> locateCollection(Environment environment) { return PropertySourceLocator.super.locateCollection(environment); } diff --git a/spring-cloud-kubernetes-fabric8-config/pom.xml b/spring-cloud-kubernetes-fabric8-config/pom.xml index 3230fa91..9ae7b7ac 100644 --- a/spring-cloud-kubernetes-fabric8-config/pom.xml +++ b/spring-cloud-kubernetes-fabric8-config/pom.xml @@ -67,6 +67,14 @@ org.springframework.security spring-security-rsa + + org.springframework.retry + spring-retry + + + org.springframework.boot + spring-boot-starter-aop + @@ -157,17 +165,6 @@ ${groovy.version} test - - - org.springframework.retry - spring-retry - test - - - org.springframework.boot - spring-boot-starter-aop - test - diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java index 1e6f9a96..b10b5797 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8BootstrapConfiguration.java @@ -29,6 +29,8 @@ import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabl import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesSecretsEnabled; import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesConfigRetryDisabled; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesSecretsRetryDisabled; import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; @@ -58,6 +60,7 @@ public class Fabric8BootstrapConfiguration { @Bean @ConditionalOnKubernetesConfigEnabled + @ConditionalOnKubernetesConfigRetryDisabled public Fabric8ConfigMapPropertySourceLocator configMapPropertySourceLocator(ConfigMapConfigProperties properties, KubernetesClient client, KubernetesNamespaceProvider provider) { return new Fabric8ConfigMapPropertySourceLocator(client, properties, provider); @@ -65,6 +68,7 @@ public class Fabric8BootstrapConfiguration { @Bean @ConditionalOnKubernetesSecretsEnabled + @ConditionalOnKubernetesSecretsRetryDisabled public Fabric8SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties, KubernetesClient client, KubernetesNamespaceProvider provider) { return new Fabric8SecretsPropertySourceLocator(client, properties, provider); diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8RetryBootstrapConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8RetryBootstrapConfiguration.java new file mode 100644 index 00000000..5e2009d1 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8RetryBootstrapConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013-2022 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.config; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.client.KubernetesClient; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform; +import org.springframework.boot.cloud.CloudPlatform; +import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled; +import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesConfigOrSecretsRetryEnabled; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesConfigRetryEnabled; +import org.springframework.cloud.kubernetes.commons.config.ConditionalOnKubernetesSecretsRetryEnabled; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * @author Ryan Baxter + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnKubernetesEnabled +@AutoConfigureBefore(Fabric8BootstrapConfiguration.class) +@Import({ KubernetesCommonsAutoConfiguration.class, Fabric8AutoConfiguration.class }) +@ConditionalOnClass({ ConfigMap.class, Secret.class }) +@AutoConfigureAfter(KubernetesBootstrapConfiguration.class) +@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) +@ConditionalOnKubernetesConfigOrSecretsRetryEnabled +public class Fabric8RetryBootstrapConfiguration { + + @Bean + @ConditionalOnKubernetesConfigRetryEnabled + public Fabric8ConfigMapPropertySourceLocator configMapPropertySourceLocator(ConfigMapConfigProperties properties, + KubernetesClient client, KubernetesNamespaceProvider provider) { + return new RetryableFabric8ConfigMapPropertySourceLocator(client, properties, provider); + } + + @Bean + @ConditionalOnKubernetesSecretsRetryEnabled + public Fabric8SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties, + KubernetesClient client, KubernetesNamespaceProvider provider) { + return new RetryableFabric8SecretsPropertySourceLocator(client, properties, provider); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java new file mode 100644 index 00000000..941ef384 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8ConfigMapPropertySourceLocator.java @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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.config; + +import java.util.Collection; + +import io.fabric8.kubernetes.client.KubernetesClient; + +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.retry.annotation.Retryable; + +/** + * ConfigMapPropertySourceLocator for when retry is enabled. + * + * @author Ryan Baxter + */ +@Order(1) +class RetryableFabric8ConfigMapPropertySourceLocator extends Fabric8ConfigMapPropertySourceLocator { + + RetryableFabric8ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties, + KubernetesNamespaceProvider provider) { + super(client, properties, provider); + } + + @Override + @Retryable(interceptor = "kubernetesConfigRetryInterceptor") + public PropertySource locate(Environment environment) { + return super.locate(environment); + } + + @Override + @Retryable(interceptor = "kubernetesConfigRetryInterceptor") + public Collection> locateCollection(Environment environment) { + return super.locateCollection(environment); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java new file mode 100644 index 00000000..880427fc --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/main/java/org/springframework/cloud/kubernetes/fabric8/config/RetryableFabric8SecretsPropertySourceLocator.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2022 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.config; + +import java.util.Collection; + +import io.fabric8.kubernetes.client.KubernetesClient; + +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.retry.annotation.Retryable; + +/** + * SecretsPropertySourceLocator for when retry is enabled. + * + * @author Ryan Baxter + */ +class RetryableFabric8SecretsPropertySourceLocator extends Fabric8SecretsPropertySourceLocator { + + RetryableFabric8SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties, + KubernetesNamespaceProvider provider) { + super(client, properties, provider); + } + + @Override + @Retryable(interceptor = "kubernetesSecretsRetryInterceptor") + public PropertySource locate(Environment environment) { + return super.locate(environment); + } + + @Override + @Retryable(interceptor = "kubernetesSecretsRetryInterceptor") + public Collection> locateCollection(Environment environment) { + return super.locateCollection(environment); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories index 79ca4b9f..cdd2220a 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.kubernetes.fabric8.config.reload.ConfigReloadAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\ -org.springframework.cloud.kubernetes.fabric8.config.Fabric8BootstrapConfiguration +org.springframework.cloud.kubernetes.fabric8.config.Fabric8BootstrapConfiguration,\ +org.springframework.cloud.kubernetes.fabric8.config.Fabric8RetryBootstrapConfiguration diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/EnableRetryBootstrapConfiguration.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/EnableRetryBootstrapConfiguration.java new file mode 100644 index 00000000..daecc5ab --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/EnableRetryBootstrapConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013-2022 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.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.retry.annotation.EnableRetry; + +/** + * @author Ryan Baxter + */ +@Order(0) +@Configuration +@ConditionalOnProperty("spring.cloud.kubernetes.test.enable-retry") +@EnableRetry +public class EnableRetryBootstrapConfiguration { + +} diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorRetryTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorRetryTests.java index cb8b56a8..1a650274 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorRetryTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8ConfigMapPropertySourceLocatorRetryTests.java @@ -16,7 +16,10 @@ package org.springframework.cloud.kubernetes.fabric8.config; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; @@ -24,17 +27,30 @@ import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.PropertySource; import org.springframework.mock.env.MockEnvironment; +import org.springframework.retry.annotation.RetryConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -227,4 +243,42 @@ public class Fabric8ConfigMapPropertySourceLocatorRetryTests { } + @Nested + @EnableKubernetesMockClient + public class EnableRetryWithoutFailFastTest { + + private ConfigurableApplicationContext context; + + protected void setup(String... env) { + List envList = (env != null) ? new ArrayList<>(Arrays.asList(env)) : new ArrayList<>(); + envList.add("spring.cloud.kubernetes.client.namespace=default"); + String[] envArray = envList.toArray(new String[0]); + + context = new SpringApplicationBuilder(RetryConfiguration.class, PropertyPlaceholderAutoConfiguration.class, + ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class, + EndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, + RefreshEndpointAutoConfiguration.class, ConfigurationPropertiesBindingPostProcessor.class, + ConfigurationPropertiesRebinderAutoConfiguration.class, Fabric8BootstrapConfiguration.class, + Fabric8RetryBootstrapConfiguration.class, KubernetesBootstrapConfiguration.class) + .web(org.springframework.boot.WebApplicationType.NONE).properties(envArray).run(); + } + + @AfterEach + public void afterEach() { + if (this.context != null) { + this.context.close(); + this.context = null; + } + } + + @Test + public void doesNotContainRetryableConfigMapPropertySourceLocator() throws Exception { + mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once(); + setup("debug=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.test.enable-retry=true"); + assertThat(context.containsBean("retryableConfigMapPropertySourceLocator")).isFalse(); + } + + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocatorRetryTests.java b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocatorRetryTests.java index a3d4232f..b840da4f 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocatorRetryTests.java +++ b/spring-cloud-kubernetes-fabric8-config/src/test/java/org/springframework/cloud/kubernetes/fabric8/config/Fabric8SecretsPropertySourceLocatorRetryTests.java @@ -16,8 +16,11 @@ package org.springframework.cloud.kubernetes.fabric8.config; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.HashMap; +import java.util.List; import java.util.Map; import io.fabric8.kubernetes.api.model.SecretBuilder; @@ -26,17 +29,30 @@ import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration; +import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration; +import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.PropertySource; import org.springframework.mock.env.MockEnvironment; +import org.springframework.retry.annotation.RetryConfiguration; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -238,4 +254,43 @@ public class Fabric8SecretsPropertySourceLocatorRetryTests { } + @Nested + @EnableKubernetesMockClient + public class EnableRetryWithoutFailFastTest { + + private ConfigurableApplicationContext context; + + protected void setup(String... env) { + List envList = (env != null) ? new ArrayList<>(Arrays.asList(env)) : new ArrayList<>(); + envList.add("spring.cloud.kubernetes.client.namespace=default"); + String[] envArray = envList.toArray(new String[0]); + + context = new SpringApplicationBuilder(RetryConfiguration.class, PropertyPlaceholderAutoConfiguration.class, + ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class, + EndpointAutoConfiguration.class, InfoEndpointAutoConfiguration.class, + RefreshEndpointAutoConfiguration.class, ConfigurationPropertiesBindingPostProcessor.class, + ConfigurationPropertiesRebinderAutoConfiguration.class, Fabric8BootstrapConfiguration.class, + Fabric8RetryBootstrapConfiguration.class, KubernetesBootstrapConfiguration.class) + .web(org.springframework.boot.WebApplicationType.NONE).properties(envArray).run(); + } + + @AfterEach + public void afterEach() { + if (this.context != null) { + this.context.close(); + this.context = null; + } + } + + @Test + public void doesNotContainRetryableSecretsPropertySourceLocator() throws Exception { + mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once(); + setup("debug=true", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.test.enable-retry=true", "spring.cloud.kubernetes.secrets.name=my-secret", + "spring.cloud.kubernetes.secrets.enable-api=true"); + assertThat(context.containsBean("retryableSecretsPropertySourceLocator")).isFalse(); + } + + } + } diff --git a/spring-cloud-kubernetes-fabric8-config/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-fabric8-config/src/test/resources/META-INF/spring.factories new file mode 100644 index 00000000..18fd93e0 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-config/src/test/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ +org.springframework.cloud.kubernetes.client.config.EnableRetryBootstrapConfiguration