Move @retryable annotations to separate beans. Fixes #963 (#969)

This commit is contained in:
Ryan Baxter
2022-02-10 20:07:50 -05:00
committed by GitHub
parent 820651afa7
commit 352d28d961
24 changed files with 674 additions and 30 deletions

View File

@@ -57,6 +57,16 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -93,12 +103,6 @@
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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<PropertySource<?>> locateCollection(Environment environment) {
return super.locateCollection(environment);
}
}

View File

@@ -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<PropertySource<?>> locateCollection(Environment environment) {
return super.locateCollection(environment);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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 {
}

View File

@@ -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<String> 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 {

View File

@@ -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<String> 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 {

View File

@@ -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

View File

@@ -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<PropertySource<?>> locateCollection(Environment environment) {
return PropertySourceLocator.super.locateCollection(environment);
}

View File

@@ -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<PropertySource<?>> locateCollection(Environment environment) {
return PropertySourceLocator.super.locateCollection(environment);
}

View File

@@ -67,6 +67,14 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-rsa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Testing Dependencies -->
<dependency>
@@ -157,17 +165,6 @@
<version>${groovy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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<PropertySource<?>> locateCollection(Environment environment) {
return super.locateCollection(environment);
}
}

View File

@@ -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<PropertySource<?>> locateCollection(Environment environment) {
return super.locateCollection(environment);
}
}

View File

@@ -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

View File

@@ -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 {
}

View File

@@ -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<String> 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();
}
}
}

View File

@@ -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<String> 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();
}
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.client.config.EnableRetryBootstrapConfiguration