Nested split (#962)

This commit is contained in:
erabii
2022-02-04 17:29:44 +02:00
committed by GitHub
parent 01879d4c47
commit bd8656c5e5
79 changed files with 4232 additions and 1714 deletions

View File

@@ -0,0 +1,59 @@
/*
* 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;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=false", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always",
"management.endpoints.web.exposure.include=health" })
class ActuatorDisabledHealthTest {
@Autowired
private ReactiveHealthContributorRegistry registry;
@Autowired
private WebTestClient webClient;
@Value("${local.server.port}")
private int port;
@Test
void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(String.class)
.value(Matchers.not(Matchers.containsString("kubernetes")));
Assertions.assertNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must NOT be present when 'management.health.kubernetes.enabled=false'");
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=true", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always", "management.endpoints.web.exposure.include=health",
"spring.main.cloud-platform=KUBERNETES" })
class ActuatorEnabledHealthTest {
@Autowired
private WebTestClient webClient;
@Autowired
private ReactiveHealthContributorRegistry registry;
@Value("${local.server.port}")
private int port;
@Test
void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(String.class)
.value(Matchers.containsString("kubernetes"));
Assertions.assertNotNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must be present when 'management.health.kubernetes.enabled=true'");
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.default_api;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import io.kubernetes.client.openapi.ApiClient;
import okhttp3.Request;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Robert McNees
*
* This tests that the apiClient created in KubernetesClientAutoConfiguration will not set
* itself as the default apiClient. This is to avoid overwriting the user's
* defaultApiClient if they include this project.
*
* kubernetes informer is disabled because KubernetesInformerAutoConfiguration creates a
* defaultApiClient that will be autowired instead of the ApiClient created in
* KubernetesClientAutoConfiguration
*/
@SpringBootTest(classes = App.class,
properties = { "kubernetes.informer.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
class ApiClientUserAgentDefaultHeader {
@Autowired
private ApiClient apiClient;
@Test
void testApiClientUserAgentDefaultHeader() throws MalformedURLException {
assertThat(apiClient).isNotNull();
Request.Builder builder = new Request.Builder();
apiClient.processHeaderParams(Collections.emptyMap(), builder);
assertThat(builder.url(new URL("http://example.com")).build().headers().get("User-Agent"))
.isEqualTo("Spring-Cloud-Kubernetes-Application");
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.default_api;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import io.kubernetes.client.openapi.ApiClient;
import okhttp3.Request;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Robert McNees
*
* This tests that the apiClient created in KubernetesClientAutoConfiguration will not set
* itself as the default apiClient. This is to avoid overwriting the user's
* defaultApiClient if they include this project.
*
* kubernetes informer is disabled because KubernetesInformerAutoConfiguration creates a
* defaultApiClient that will be autowired instead of the ApiClient created in
* KubernetesClientAutoConfiguration
*/
@SpringBootTest(classes = App.class, properties = { "kubernetes.informer.enabled=false",
"spring.cloud.kubernetes.client.userAgent=non-default", "spring.main.cloud-platform=KUBERNETES" })
class ApiClientUserAgentNonDefaultHeader {
@Autowired
private ApiClient apiClient;
@Autowired
private ConfigurableApplicationContext context;
@Test
void testApiClientUserAgentDefaultHeader() throws MalformedURLException {
assertThat(apiClient).isNotNull();
Request.Builder builder = new Request.Builder();
apiClient.processHeaderParams(Collections.emptyMap(), builder);
assertThat(builder.url(new URL("http://example.com")).build().headers().get("User-Agent"))
.isEqualTo("non-default");
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.default_api;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
class App {
}

View File

@@ -0,0 +1,55 @@
/*
* 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.default_api;
import io.kubernetes.client.openapi.ApiClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Robert McNees
*
* This tests that the apiClient created in KubernetesClientAutoConfiguration will not set
* itself as the default apiClient. This is to avoid overwriting the user's
* defaultApiClient if they include this project.
*
* kubernetes informer is disabled because KubernetesInformerAutoConfiguration creates a
* defaultApiClient that will be autowired instead of the ApiClient created in
* KubernetesClientAutoConfiguration
*/
@SpringBootTest(classes = App.class,
properties = { "kubernetes.informer.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
class DefaultApiClientNotSameAsApiClient {
@Autowired
private ApiClient apiClient;
@Test
void testCreatedApiClientIsNotDefault() {
assertThat(apiClient).isNotNull();
ApiClient defaultApiClient = io.kubernetes.client.openapi.Configuration.getDefaultApiClient();
assertThat(defaultApiClient).isNotNull();
assertThat(defaultApiClient).isNotSameAs(apiClient);
}
}

View File

@@ -1,127 +0,0 @@
/*
* Copyright 2013-2021 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.default_api;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import io.kubernetes.client.openapi.ApiClient;
import okhttp3.Request;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Robert McNees
*
* This tests that the apiClient created in KubernetesClientAutoConfiguration will not set
* itself as the default apiClient. This is to avoid overwriting the user's
* defaultApiClient if they include this project.
*
* kubernetes informer is disabled because KubernetesInformerAutoConfiguration creates a
* defaultApiClient that will be autowired instead of the ApiClient created in
* KubernetesClientAutoConfiguration
*/
class KubernetesClientDefaultApiClientTests {
private static final String DISABLE_INFORMER = "kubernetes.informer.enabled=false";
private static final String USER_AGENT = "spring.cloud.kubernetes.client.userAgent=non-default";
private static final String ENABLED_K8S = "spring.main.cloud-platform=KUBERNETES";
@SpringBootTest(classes = KubernetesClientDefaultApiClientTests.App.class,
properties = { DISABLE_INFORMER, ENABLED_K8S })
@Nested
class DefaultApiClientNotSameAsApiClient {
@Autowired
private ApiClient apiClient;
@Autowired
ConfigurableApplicationContext context;
@Test
void testCreatedApiClientIsNotDefault() {
assertThat(apiClient).isNotNull();
ApiClient defaultApiClient = io.kubernetes.client.openapi.Configuration.getDefaultApiClient();
assertThat(defaultApiClient).isNotNull();
assertThat(defaultApiClient).isNotSameAs(apiClient);
}
}
@SpringBootTest(classes = KubernetesClientDefaultApiClientTests.App.class,
properties = { DISABLE_INFORMER, ENABLED_K8S })
@Nested
class ApiClientUserAgentDefaultHeader {
@Autowired
private ApiClient apiClient;
@Autowired
ConfigurableApplicationContext context;
@Test
void testApiClientUserAgentDefaultHeader() throws MalformedURLException {
assertThat(apiClient).isNotNull();
Request.Builder builder = new Request.Builder();
apiClient.processHeaderParams(Collections.emptyMap(), builder);
assertThat(builder.url(new URL("http://example.com")).build().headers().get("User-Agent"))
.isEqualTo("Spring-Cloud-Kubernetes-Application");
}
}
@SpringBootTest(classes = KubernetesClientDefaultApiClientTests.App.class,
properties = { DISABLE_INFORMER, USER_AGENT, ENABLED_K8S })
@Nested
class ApiClientUserAgentNonDefaultHeader {
@Autowired
private ApiClient apiClient;
@Autowired
ConfigurableApplicationContext context;
@Test
void testApiClientUserAgentDefaultHeader() throws MalformedURLException {
assertThat(apiClient).isNotNull();
Request.Builder builder = new Request.Builder();
apiClient.processHeaderParams(Collections.emptyMap(), builder);
assertThat(builder.url(new URL("http://example.com")).build().headers().get("User-Agent"))
.isEqualTo("non-default");
}
}
@SpringBootApplication
static class App {
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.SpringBootApplication;
@SpringBootApplication
public class Application {
}

View File

@@ -1,175 +0,0 @@
/*
* 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 org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
public class KubernetesClientBootstrapConfigurationTests {
@SpringBootApplication
static class Application {
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "kubernetes.informer.enabled=false", "kubernetes.manifests.enabled=false" })
@Nested
class KubernetesDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansAreNotPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=true",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledOnPurpose {
@Autowired
ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.config.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledConfigDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledSecretsDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false",
"spring.cloud.kubernetes.config.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledSecretsAndConfigDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is enabled, both property sources are present
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=abc" })
@Nested
class KubernetesClientBootstrapConfigurationInsideK8s {
@Autowired
ConfigurableApplicationContext context;
@Test
public void bothPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is disabled, no property source bean is present
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "kubernetes.manifests.enabled=false" })
@Nested
class KubernetesClientBootstrapConfigurationNotInsideK8s {
@Autowired
ConfigurableApplicationContext context;
@Test
public void bothMissing() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}
}

View File

@@ -1,282 +0,0 @@
/*
* Copyright 2013-2021 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.HashMap;
import java.util.Map;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
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.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
public class KubernetesClientConfigMapPropertySourceLocatorRetryTests {
private static final String API = "/api/v1/namespaces/default/configmaps";
private static final String SECRETS_API = "/api/v1/namespaces/default/secrets";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
stubFor(get(SECRETS_API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
}
@AfterAll
public static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
public void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true",
"spring.cloud.kubernetes.config.retry.max-attempts=5", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigRetryEnabled {
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Test
public void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, String> data = new HashMap<>();
data.put("some.prop", "theValue");
data.put("some.number", "0");
V1ConfigMapList configMapList = new V1ConfigMapList()
.addItemsItem(new V1ConfigMap().metadata(new V1ObjectMeta().name("application")).data(data));
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.prop")).isEqualTo("theValue");
assertThat(propertySource.getProperty("some.number")).isEqualTo("0");
}
@Test
public void locateShouldRetryAndRecover() {
Map<String, String> data = new HashMap<>();
data.put("some.prop", "theValue");
data.put("some.number", "0");
V1ConfigMapList configMapList = new V1ConfigMapList()
.addItemsItem(new V1ConfigMap().metadata(new V1ObjectMeta().name("application")).data(data));
// fail 3 times
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs(STARTED)
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed once"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed once")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed twice"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed twice")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed thrice"));
// then succeed
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed thrice")
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.prop")).isEqualTo("theValue");
assertThat(propertySource.getProperty("some.number")).isEqualTo("0");
}
@Test
public void locateShouldRetryAndFail() {
// fail all the 5 requests
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}
@Nested
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigFailFastDisabled {
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Test
public void locateShouldNotRetry() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true",
"spring.cloud.kubernetes.config.retry.enabled=false",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigRetryDisabledButSecretsRetryEnabled {
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
public void locateShouldFailWithoutRetrying() {
/*
* Enabling secrets retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for config maps to be
* defined. ConfigMapPropertySourceLocator should not retry even Spring Retry
* is enabled.
*/
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesConfigRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true",
"spring.cloud.kubernetes.config.retry.enabled=false", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigFailFastEnabledButRetryDisabled {
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
public void locateShouldFailWithoutRetrying() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesConfigRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@SpringBootApplication
static class App {
}
}

View File

@@ -1,284 +0,0 @@
/*
* Copyright 2013-2021 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.HashMap;
import java.util.Map;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
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.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
public class KubernetesClientSecretsPropertySourceLocatorRetryTests {
private static final String API = "/api/v1/namespaces/default/secrets";
private static final String CONFIG_MAPS_API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
stubFor(get(CONFIG_MAPS_API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
}
@AfterAll
public static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
public void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.max-attempts=5", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class SecretsRetryEnabled {
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Test
public void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, byte[]> data = new HashMap<>();
data.put("some.sensitive.prop", "theSensitiveValue".getBytes());
data.put("some.sensitive.number", "1".getBytes());
V1SecretList secretList = new V1SecretList()
.addItemsItem(new V1Secret().metadata(new V1ObjectMeta().name("my-secret")).data(data));
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secretList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
public void locateShouldRetryAndRecover() {
Map<String, byte[]> data = new HashMap<>();
data.put("some.sensitive.prop", "theSensitiveValue".getBytes());
data.put("some.sensitive.number", "1".getBytes());
V1SecretList secretList = new V1SecretList()
.addItemsItem(new V1Secret().metadata(new V1ObjectMeta().name("my-secret")).data(data));
// fail 3 times
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs(STARTED)
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed once"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed once")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed twice"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed twice")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed thrice"));
// then succeed
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed thrice")
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secretList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
public void locateShouldRetryAndFail() {
// fail all the 5 requests
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.name=my-secret", "spring.cloud.kubernetes.secrets.enable-api=true",
"spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class SecretsFailFastDisabled {
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Test
public void locateShouldNotRetry() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.enabled=false",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class SecretsRetryDisabledButConfigRetryEnabled {
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
public void locateShouldFailWithoutRetrying() {
/*
* Enabling config retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for secrets to be
* defined. SecretsPropertySourceLocator should not retry even Spring Retry is
* enabled.
*/
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.enabled=false", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class SecretsFailFastEnabledButRetryDisabled {
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
public void locateShouldFailWithoutRetrying() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@SpringBootApplication
static class App {
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=abc" })
class KubernetesClientBootstrapConfigurationInsideK8s {
@Autowired
private ConfigurableApplicationContext context;
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is enabled, both property sources are present
@Test
public void bothPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "kubernetes.manifests.enabled=false" })
class KubernetesClientBootstrapConfigurationNotInsideK8s {
@Autowired
private ConfigurableApplicationContext context;
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is disabled, no property source bean is present
@Test
void bothMissing() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.enabled=false", "kubernetes.informer.enabled=false",
"kubernetes.manifests.enabled=false" })
class KubernetesDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansAreNotPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.config.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledConfigDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=true",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledOnPurpose {
@Autowired
private ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false", "spring.cloud.kubernetes.config.enabled=false",
"spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledSecretsAndConfigDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.boostrap_configuration;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.client.config.Application;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledSecretsDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(KubernetesClientConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesClientSecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.configmap_retry;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
class App {
}

View File

@@ -0,0 +1,103 @@
/*
* 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.configmap_retry;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigFailFastDisabled {
private static final String API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetry() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.configmap_retry;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.enabled=false",
"spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigFailFastEnabledButRetryDisabled {
private static final String API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesConfigRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.configmap_retry;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.enabled=false",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigRetryDisabledButSecretsRetryEnabled {
private static final String API = "/api/v1/namespaces/default/configmaps";
private static final String SECRETS_API = "/api/v1/namespaces/default/secrets";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
stubFor(get(SECRETS_API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
/*
* Enabling secrets retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for config maps to be
* defined. ConfigMapPropertySourceLocator should not retry even Spring Retry is
* enabled.
*/
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesConfigRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.configmap_retry;
import java.util.HashMap;
import java.util.Map;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.max-attempts=5",
"spring.main.cloud-platform=KUBERNETES" },
classes = App.class)
class ConfigRetryEnabled {
private static final String API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, String> data = new HashMap<>();
data.put("some.prop", "theValue");
data.put("some.number", "0");
V1ConfigMapList configMapList = new V1ConfigMapList()
.addItemsItem(new V1ConfigMap().metadata(new V1ObjectMeta().name("application")).data(data));
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.prop")).isEqualTo("theValue");
assertThat(propertySource.getProperty("some.number")).isEqualTo("0");
}
@Test
void locateShouldRetryAndRecover() {
Map<String, String> data = new HashMap<>();
data.put("some.prop", "theValue");
data.put("some.number", "0");
V1ConfigMapList configMapList = new V1ConfigMapList()
.addItemsItem(new V1ConfigMap().metadata(new V1ObjectMeta().name("application")).data(data));
// fail 3 times
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs(STARTED)
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed once"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed once")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed twice"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed twice")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed thrice"));
// then succeed
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed thrice")
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(configMapList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.prop")).isEqualTo("theValue");
assertThat(propertySource.getProperty("some.number")).isEqualTo("0");
}
@Test
void locateShouldRetryAndFail() {
// fail all the 5 requests
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.secrets_retry;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
class Application {
}

View File

@@ -0,0 +1,105 @@
/*
* 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.secrets_retry;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.name=my-secret", "spring.cloud.kubernetes.secrets.enable-api=true",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
class SecretsFailFastDisabled {
private static final String API = "/api/v1/namespaces/default/secrets";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetry() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2013-2021 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.secrets_retry;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.cloud.kubernetes.secrets.retry.enabled=false",
"spring.cloud.kubernetes.secrets.name=my-secret", "spring.cloud.kubernetes.secrets.enable-api=true",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
class SecretsFailFastEnabledButRetryDisabled {
private static final String API = "/api/v1/namespaces/default/secrets";
private static final String CONFIG_MAPS_API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
stubFor(get(CONFIG_MAPS_API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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.secrets_retry;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.cloud.kubernetes.secrets.retry.enabled=false",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
class SecretsRetryDisabledButConfigRetryEnabled {
private static final String API = "/api/v1/namespaces/default/secrets";
private static final String CONFIG_MAPS_API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
stubFor(get(CONFIG_MAPS_API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1ConfigMapList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
/*
* Enabling config retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for secrets to be
* defined. SecretsPropertySourceLocator should not retry even Spring Retry is
* enabled.
*/
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.secrets_retry;
import java.util.HashMap;
import java.util.Map;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.max-attempts=5", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
class SecretsRetryEnabled {
private static final String API = "/api/v1/namespaces/default/secrets";
private static WireMockServer wireMockServer;
private static MockedStatic<KubernetesClientUtils> clientUtilsMock;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
stubConfigMapAndSecretsDefaults();
}
private static void stubConfigMapAndSecretsDefaults() {
// return empty config map / secret list to not fail context creation
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(new V1SecretList()))));
}
@AfterAll
static void teardown() {
wireMockServer.stop();
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
stubConfigMapAndSecretsDefaults();
}
@SpyBean
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, byte[]> data = new HashMap<>();
data.put("some.sensitive.prop", "theSensitiveValue".getBytes());
data.put("some.sensitive.number", "1".getBytes());
V1SecretList secretList = new V1SecretList()
.addItemsItem(new V1Secret().metadata(new V1ObjectMeta().name("my-secret")).data(data));
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secretList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
void locateShouldRetryAndRecover() {
Map<String, byte[]> data = new HashMap<>();
data.put("some.sensitive.prop", "theSensitiveValue".getBytes());
data.put("some.sensitive.number", "1".getBytes());
V1SecretList secretList = new V1SecretList()
.addItemsItem(new V1Secret().metadata(new V1ObjectMeta().name("my-secret")).data(data));
// fail 3 times
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs(STARTED)
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed once"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed once")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed twice"));
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed twice")
.willReturn(aResponse().withStatus(500)).willSetStateTo("Failed thrice"));
// then succeed
stubFor(get(API).inScenario("Retry and Recover").whenScenarioStateIs("Failed thrice")
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(secretList))));
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
void locateShouldRetryAndFail() {
// fail all the 5 requests
stubFor(get(API).willReturn(aResponse().withStatus(500).withBody("Internal Server Error")));
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.commons.config;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
}

View File

@@ -0,0 +1,79 @@
/*
* 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.commons.config.bootstrap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.AbstractConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class,
properties = { "spring.cloud.kubernetes.config.fail-fast=true",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.main.cloud-platform=KUBERNETES" })
public class ConfigAndSecretsFailFastEnabledWithDefaultRetryConfiguration {
@Autowired
private ConfigurableApplicationContext context;
@Autowired
private ConfigMapConfigProperties configMapConfigProperties;
@Autowired
private SecretsConfigProperties secretsConfigProperties;
@Test
void shouldDefineRequiredBeans() {
Map<String, RetryOperationsInterceptor> retryInterceptors = context
.getBeansOfType(RetryOperationsInterceptor.class);
assertThat(retryInterceptors.containsKey("kubernetesConfigRetryInterceptor")).isTrue();
assertThat(retryInterceptors.containsKey("kubernetesSecretsRetryInterceptor")).isTrue();
}
@Test
void retryConfigurationShouldBeDefault() {
AbstractConfigProperties.RetryProperties defaultRetryProperties = new AbstractConfigProperties.RetryProperties();
AbstractConfigProperties.RetryProperties configMapRetryProperties = configMapConfigProperties.getRetry();
assertThat(configMapRetryProperties.getMaxAttempts()).isEqualTo(defaultRetryProperties.getMaxAttempts());
assertThat(configMapRetryProperties.getInitialInterval())
.isEqualTo(defaultRetryProperties.getInitialInterval());
assertThat(configMapRetryProperties.getMaxInterval()).isEqualTo(defaultRetryProperties.getMaxInterval());
assertThat(configMapRetryProperties.getMultiplier()).isEqualTo(defaultRetryProperties.getMultiplier());
AbstractConfigProperties.RetryProperties secretsRetryProperties = secretsConfigProperties.getRetry();
assertThat(secretsRetryProperties.getMaxAttempts()).isEqualTo(defaultRetryProperties.getMaxAttempts());
assertThat(secretsRetryProperties.getInitialInterval()).isEqualTo(defaultRetryProperties.getInitialInterval());
assertThat(secretsRetryProperties.getMaxInterval()).isEqualTo(defaultRetryProperties.getMaxInterval());
assertThat(secretsRetryProperties.getMultiplier()).isEqualTo(defaultRetryProperties.getMultiplier());
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.commons.config.bootstrap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.AbstractConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class,
properties = { "spring.cloud.kubernetes.config.fail-fast=true", "spring.main.cloud-platform=KUBERNETES" })
class ConfigFailFastEnabled {
@Autowired
private ConfigurableApplicationContext context;
@Autowired
private ConfigMapConfigProperties configMapConfigProperties;
@Test
void shouldDefineRequiredBeans() {
Map<String, RetryOperationsInterceptor> retryInterceptors = context
.getBeansOfType(RetryOperationsInterceptor.class);
assertThat(retryInterceptors.containsKey("kubernetesConfigRetryInterceptor")).isTrue();
assertThat(retryInterceptors.containsKey("kubernetesSecretsRetryInterceptor")).isTrue();
}
@Test
void retryConfigurationShouldBeDefault() {
AbstractConfigProperties.RetryProperties retryProperties = configMapConfigProperties.getRetry();
AbstractConfigProperties.RetryProperties defaultRetryProperties = new AbstractConfigProperties.RetryProperties();
assertThat(retryProperties.getMaxAttempts()).isEqualTo(defaultRetryProperties.getMaxAttempts());
assertThat(retryProperties.getInitialInterval()).isEqualTo(defaultRetryProperties.getInitialInterval());
assertThat(retryProperties.getMaxInterval()).isEqualTo(defaultRetryProperties.getMaxInterval());
assertThat(retryProperties.getMultiplier()).isEqualTo(defaultRetryProperties.getMultiplier());
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.commons.config.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class, properties = {
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.enabled=false" })
class ConfigFailFastEnabledButRetryDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void shouldNotDefineRetryBeans() {
assertThat(context.getBeansOfType(RetryOperationsInterceptor.class)).isEmpty();
}
}

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.commons.config.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.AbstractConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class,
properties = { "spring.cloud.kubernetes.config.fail-fast=true",
"spring.cloud.kubernetes.config.retry.max-attempts=3",
"spring.cloud.kubernetes.config.retry.initial-interval=1500",
"spring.cloud.kubernetes.config.retry.max-interval=3000",
"spring.cloud.kubernetes.config.retry.multiplier=1.5", "spring.main.cloud-platform=KUBERNETES" })
class ConfigFailFastEnabledWithCustomRetryConfiguration {
@Autowired
private ConfigMapConfigProperties configMapConfigProperties;
@Test
void retryConfigurationShouldBeCustomized() {
AbstractConfigProperties.RetryProperties retryProperties = configMapConfigProperties.getRetry();
assertThat(retryProperties.getMaxAttempts()).isEqualTo(3);
assertThat(retryProperties.getInitialInterval()).isEqualTo(1500L);
assertThat(retryProperties.getMaxInterval()).isEqualTo(3000L);
assertThat(retryProperties.getMultiplier()).isEqualTo(1.5D);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.commons.config.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class)
class FailFastDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void shouldNotDefineRetryBeans() {
assertThat(context.getBeansOfType(RetryOperationsInterceptor.class)).isEmpty();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2013-2021 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.commons.config.bootstrap;
import org.aspectj.lang.annotation.Aspect;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
class FailFastEnabledWithoutSpringRetryOnClasspath {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(KubernetesBootstrapConfiguration.class))
.withClassLoader(new FilteredClassLoader(Retryable.class, Aspect.class, AopAutoConfiguration.class));
@Test
void shouldNotDefineRetryBeansWhenConfigMapFailFastEnabled() {
contextRunner.withPropertyValues("spring.cloud.kubernetes.config.fail-fast=true")
.run(context -> assertThat(context.getBeansOfType(RetryOperationsInterceptor.class)).isEmpty());
}
@Test
void shouldNotDefineRetryBeansWhenSecretsFailFastEnabled() {
contextRunner.withPropertyValues("spring.cloud.kubernetes.secrets.fail-fast=true")
.run(context -> assertThat(context.getBeansOfType(RetryOperationsInterceptor.class)).isEmpty());
}
@Test
void shouldNotDefineRetryBeansWhenConfigMapAndSecretsFailFastEnabled() {
contextRunner
.withPropertyValues("spring.cloud.kubernetes.config.fail-fast=true",
"spring.cloud.kubernetes.secrets.fail-fast=true")
.run(context -> assertThat(context.getBeansOfType(RetryOperationsInterceptor.class)).isEmpty());
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2013-2021 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.commons.config.bootstrap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.AbstractConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class,
properties = { "spring.cloud.kubernetes.secrets.fail-fast=true", "spring.main.cloud-platform=KUBERNETES" })
class SecretsFailFastEnabled {
@Autowired
private ConfigurableApplicationContext context;
@Autowired
private SecretsConfigProperties secretsConfigProperties;
@Test
void shouldDefineRequiredBeans() {
Map<String, RetryOperationsInterceptor> retryInterceptors = context
.getBeansOfType(RetryOperationsInterceptor.class);
assertThat(retryInterceptors.containsKey("kubernetesConfigRetryInterceptor")).isTrue();
assertThat(retryInterceptors.containsKey("kubernetesSecretsRetryInterceptor")).isTrue();
}
@Test
void retryConfigurationShouldBeDefault() {
AbstractConfigProperties.RetryProperties retryProperties = secretsConfigProperties.getRetry();
AbstractConfigProperties.RetryProperties defaultRetryProperties = new AbstractConfigProperties.RetryProperties();
assertThat(retryProperties.getMaxAttempts()).isEqualTo(defaultRetryProperties.getMaxAttempts());
assertThat(retryProperties.getInitialInterval()).isEqualTo(defaultRetryProperties.getInitialInterval());
assertThat(retryProperties.getMaxInterval()).isEqualTo(defaultRetryProperties.getMaxInterval());
assertThat(retryProperties.getMultiplier()).isEqualTo(defaultRetryProperties.getMultiplier());
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2013-2021 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.commons.config.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class, properties = {
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.cloud.kubernetes.secrets.retry.enabled=false" })
class SecretsFailFastEnabledButRetryDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void shouldNotDefineRetryBeans() {
assertThat(context.getBeansOfType(RetryOperationsInterceptor.class)).isEmpty();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2013-2021 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.commons.config.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.commons.config.AbstractConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.App;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = App.class,
properties = { "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.max-attempts=3",
"spring.cloud.kubernetes.secrets.retry.initial-interval=1500",
"spring.cloud.kubernetes.secrets.retry.max-interval=3000",
"spring.cloud.kubernetes.secrets.retry.multiplier=1.5", "spring.main.cloud-platform=KUBERNETES" })
class SecretsFailFastEnabledWithCustomRetryConfiguration {
@Autowired
private SecretsConfigProperties secretsConfigProperties;
@Test
void retryConfigurationShouldBeCustomized() {
AbstractConfigProperties.RetryProperties retryProperties = secretsConfigProperties.getRetry();
assertThat(retryProperties.getMaxAttempts()).isEqualTo(3);
assertThat(retryProperties.getInitialInterval()).isEqualTo(1500L);
assertThat(retryProperties.getMaxInterval()).isEqualTo(3000L);
assertThat(retryProperties.getMultiplier()).isEqualTo(1.5D);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.profiles.include=kubernetes,kubernetesdisabled", "spring.cloud.kubernetes.enabled=false",
"debug=true" },
classes = { KubernetesConfigServerApplication.class, MockConfig.class })
class ConfigServerAutoConfigurationKubernetesDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default" })
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncluded {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.config.enableApi=false" })
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedConfigApiDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class }, properties = { "spring.main.cloud-platform=KUBERNETES",
"spring.profiles.include=kubernetes", "debug=true", "spring.cloud.kubernetes.config.enabled=false" })
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedConfigMapDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default" })
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedSecretsApiDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)[0])
.isEqualTo("configMapPropertySourceSupplier");
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.profiles.include=kubernetes", "debug=true", "spring.main.cloud-platform=KUBERNETES",
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.enableApi=true" })
class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedSecretsApiEnabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(2);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.configserver;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class, MockConfig.class },
properties = { "spring.cloud.kubernetes.enabled=false", "spring.profiles.include=kubernetes,kubernetesdisabled",
"debug=true" })
class ConfigServerAutoConfigurationKubernetesProfileMissing {
@Autowired
private ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
}
}

View File

@@ -75,7 +75,7 @@ public class ConfigServerIntegrationTest {
}
@Test
public void enabled() throws Exception {
public void enabled() {
Environment env = testRestTemplate.getForObject("/test-cm/default", Environment.class);
assertThat(env.getPropertySources().size()).isEqualTo(2);
assertThat(env.getPropertySources().get(0).getName().equals("configmap.test-cm.default")).isTrue();

View File

@@ -1,176 +0,0 @@
/*
* Copyright 2013-2021 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.configserver;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* @author Ryan Baxter
*/
public class KubernetesConfigServerAutoConfigurationTests {
@Configuration
static class MockConfig {
@Bean
@Profile("kubernetesdisabled")
public EnvironmentRepository environmentRepository() {
return mock(EnvironmentRepository.class);
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.profiles.include=kubernetes,kubernetesdisabled", "debug=true" },
classes = { KubernetesConfigServerApplication.class, MockConfig.class })
public class KubernetesDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class, MockConfig.class },
properties = { "spring.profiles.include=kubernetes,kubernetesdisabled", "debug=true" })
@Nested
class KubernetesProfileMissing {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default" })
@Nested
class KubernetesEnabledProfileIncluded {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", // STOPSHIP: 11/10/21
"spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.config.enabled=false" })
@Nested
class KubernetesEnabledProfileIncludedConfigMapDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default" })
@Nested
class KubernetesEnabledProfileIncludedSecretsApiDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)[0])
.isEqualTo("configMapPropertySourceSupplier");
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.enableApi=true" })
@Nested
class KubernetesEnabledProfileIncludedSecretsApiEnabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(2);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes", "debug=true",
"spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.enableApi=false" })
@Nested
class KubernetesEnabledProfileIncludedConfigApiDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
void runTest() {
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.configserver;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import static org.mockito.Mockito.mock;
@Configuration
class MockConfig {
@Bean
@Profile("kubernetesdisabled")
public EnvironmentRepository environmentRepository() {
return mock(EnvironmentRepository.class);
}
}

View File

@@ -22,7 +22,6 @@ import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -32,11 +31,9 @@ import org.springframework.cloud.kubernetes.fabric8.Fabric8HealthIndicator;
import org.springframework.cloud.kubernetes.fabric8.Fabric8InfoContributor;
import org.springframework.cloud.kubernetes.fabric8.Fabric8PodUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.password=mypassword",
"spring.cloud.kubernetes.client.proxy-password=myproxypassword" })

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2013-2021 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;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.example.App;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*
* test "User-Agent" functionality
*/
class Fabric8ClientUserAgentTests {
private static final String USER_AGENT = "spring.cloud.kubernetes.client.userAgent=non-default";
private static final String ENABLED_K8S = "spring.main.cloud-platform=KUBERNETES";
@Nested
@SpringBootTest(classes = App.class, properties = ENABLED_K8S)
class DefaultConfigurationForClient {
@Autowired
private KubernetesClient client;
@Test
void testUserAgent() {
String userAgent = client.getConfiguration().getUserAgent();
assertThat(userAgent).isEqualTo("Spring-Cloud-Kubernetes-Application");
}
}
@Nested
@SpringBootTest(classes = App.class, properties = { USER_AGENT, ENABLED_K8S })
class PropertiesConfigurationForClient {
@Autowired
private KubernetesClient client;
@Test
void testUserAgent() {
String userAgent = client.getConfiguration().getUserAgent();
assertThat(userAgent).isEqualTo("non-default");
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.example.App;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(classes = App.class, properties = "spring.main.cloud-platform=KUBERNETES")
class Fabric8UserAgentDefaultConfigurationTests {
@Autowired
private KubernetesClient client;
@Test
void testUserAgent() {
String userAgent = client.getConfiguration().getUserAgent();
assertThat(userAgent).isEqualTo("Spring-Cloud-Kubernetes-Application");
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.example.App;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(classes = App.class, properties = { "spring.cloud.kubernetes.client.userAgent=non-default",
"spring.main.cloud-platform=KUBERNETES" })
class Fabric8UserAgentPropertiesConfiguration {
@Autowired
private KubernetesClient client;
@Test
void testUserAgent() {
String userAgent = client.getConfiguration().getUserAgent();
assertThat(userAgent).isEqualTo("non-default");
}
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright 2013-2021 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.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
/**
* @author wind57
*/
public class Fabric8ActuatorTests {
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"management.health.kubernetes.enabled=false", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always", "management.endpoints.web.exposure.include=health",
"spring.cloud.kubernetes.client.namespace=default" })
public class DisabledHealthTest {
@Autowired
private ReactiveHealthContributorRegistry registry;
@Autowired
private WebTestClient webClient;
@Value("${local.server.port}")
private int port;
@Test
public void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(String.class)
.value(not(containsString("kubernetes")));
Assertions.assertNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must NOT be present when 'management.health.kubernetes.enabled=false'");
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"spring.main.cloud-platform=KUBERNETES", "management.health.kubernetes.enabled=true",
"management.endpoint.health.show-details=always", "management.endpoint.health.show-components=always",
"management.endpoints.web.exposure.include=health", "spring.cloud.kubernetes.client.namespace=default" })
public class EnabledHealthTest {
@Autowired
private WebTestClient webClient;
@Autowired
private ReactiveHealthContributorRegistry registry;
@Value("${local.server.port}")
private int port;
@Test
public void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(String.class)
.value(containsString("kubernetes"));
Assertions.assertNotNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must be present when 'management.health.kubernetes.enabled=true'");
}
}
}

View File

@@ -1,167 +0,0 @@
/*
* Copyright 2013-2021 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.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
public class Fabric8BootstrapConfigurationTests {
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
@Nested
class KubernetesDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void configAndSecretsBeansAreNotPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=true",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledOnPurpose {
@Autowired
ConfigurableApplicationContext context;
@Test
public void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.config.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledConfigDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
@Nested
class KubernetesEnabledSecretsDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false",
"spring.cloud.kubernetes.config.enabled=false" })
@Nested
class KubernetesEnabledSecretsAndConfigDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is enabled, both property sources are present
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=abc" })
@Nested
class Fabric8BootstrapConfigurationInsideK8s {
@Autowired
ConfigurableApplicationContext context;
@Test
public void bothPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is disabled, no property source bean is present
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
@Nested
class Fabric8BootstrapConfigurationNotInsideK8s {
@Autowired
ConfigurableApplicationContext context;
@Test
public void bothMissing() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}
}

View File

@@ -1,241 +0,0 @@
/*
* Copyright 2013-2021 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.Base64;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.SecretListBuilder;
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.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.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@EnableKubernetesMockClient
public class Fabric8SecretsPropertySourceLocatorRetryTests {
private static final String API = "/api/v1/namespaces/default/secrets/my-secret";
private static final String LIST_API = "/api/v1/namespaces/default/secrets";
static KubernetesMockServer mockServer;
static KubernetesClient mockClient;
@BeforeAll
public static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
// return empty secret list to not fail context creation
mockServer.expect().withPath(LIST_API).andReturn(200, new SecretListBuilder().build()).always();
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.max-attempts=5", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsRetryEnabled {
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Test
public void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, String> data = new HashMap<>();
data.put("some.sensitive.prop", Base64.getEncoder().encodeToString("theSensitiveValue".getBytes()));
data.put("some.sensitive.number", Base64.getEncoder().encodeToString("1".getBytes()));
// return secret without failing
mockServer.expect().withPath(API).andReturn(200,
new SecretBuilder().withNewMetadata().withName("my-secret").endMetadata().addToData(data).build())
.once();
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
public void locateShouldRetryAndRecover() {
Map<String, String> data = new HashMap<>();
data.put("some.sensitive.prop", Base64.getEncoder().encodeToString("theSensitiveValue".getBytes()));
data.put("some.sensitive.number", Base64.getEncoder().encodeToString("1".getBytes()));
// fail 3 times then succeed at the 4th call
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").times(3);
mockServer.expect().withPath(API).andReturn(200,
new SecretBuilder().withNewMetadata().withName("my-secret").endMetadata().addToData(data).build())
.once();
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
public void locateShouldRetryAndFail() {
// fail all the 5 requests
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").times(5);
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.name=my-secret", "spring.cloud.kubernetes.secrets.enable-api=true",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsFailFastDisabled {
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Test
public void locateShouldNotRetry() {
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.enabled=false",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsRetryDisabledButConfigRetryEnabled {
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
public void locateShouldFailWithoutRetrying() {
/*
* Enabling config retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for secrets to be
* defined. SecretsPropertySourceLocator should not retry even Spring Retry is
* enabled.
*/
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
@Nested
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.enabled=false", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsFailFastEnabledButRetryDisabled {
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
public void locateShouldFailWithoutRetrying() {
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.actuator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=false", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always", "management.endpoints.web.exposure.include=health",
"spring.cloud.kubernetes.client.namespace=default" })
class DisabledHealthTest {
@Autowired
private ReactiveHealthContributorRegistry registry;
@Autowired
private WebTestClient webClient;
@Value("${local.server.port}")
private int port;
@Test
void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(String.class)
.value(not(containsString("kubernetes")));
Assertions.assertNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must NOT be present when 'management.health.kubernetes.enabled=false'");
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.actuator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.ReactiveHealthContributorRegistry;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.hamcrest.Matchers.containsString;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "management.health.kubernetes.enabled=true", "management.endpoint.health.show-details=always",
"management.endpoint.health.show-components=always", "management.endpoints.web.exposure.include=health",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class EnabledHealthTest {
@Autowired
private WebTestClient webClient;
@Autowired
private ReactiveHealthContributorRegistry registry;
@Value("${local.server.port}")
private int port;
@Test
void healthEndpointShouldContainKubernetes() {
this.webClient.get().uri("http://localhost:{port}/actuator/health", this.port)
.accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(String.class)
.value(containsString("kubernetes"));
Assertions.assertNotNull(registry.getContributor("kubernetes"),
"reactive kubernetes contributor must be present when 'management.health.kubernetes.enabled=true'");
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=abc" })
class Fabric8BootstrapConfigurationInsideK8s {
@Autowired
private ConfigurableApplicationContext context;
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is enabled, both property sources are present
@Test
void bothPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class)
class Fabric8BootstrapConfigurationNotInsideK8s {
@Autowired
private ConfigurableApplicationContext context;
// tests that @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) has the desired
// effect, meaning when it is disabled, no property source bean is present
@Test
void bothMissing() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = "spring.cloud.kubernetes.enabled=false")
class KubernetesDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansAreNotPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2013-2021 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.config.enabled=false", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledConfigDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=true",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledOnPurpose {
@Autowired
private ConfigurableApplicationContext context;
@Test
void configAndSecretsBeansArePresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class, properties = {
"spring.cloud.kubernetes.secrets.enabled=false", "spring.cloud.kubernetes.config.enabled=false" })
class KubernetesEnabledSecretsAndConfigDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(0);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.bootstrap;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Application.class,
properties = { "spring.cloud.kubernetes.secrets.enabled=false",
"spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" })
class KubernetesEnabledSecretsDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void secretsOnlyPresent() {
assertThat(context.getBeanNamesForType(Fabric8ConfigMapPropertySourceLocator.class)).hasSize(1);
assertThat(context.getBeanNamesForType(Fabric8SecretsPropertySourceLocator.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.locator_retry;
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.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class ConfigFailFastDisabled {
private static final String API = "/api/v1/namespaces/default/configmaps/application";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
}
@SpyBean
private Fabric8ConfigMapPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetry() {
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.locator_retry;
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.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.enabled=false",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class ConfigFailFastEnabledButRetryDisabled {
private static final String API = "/api/v1/namespaces/default/configmaps/application";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
}
@SpyBean
private Fabric8ConfigMapPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
assertThat(context.containsBean("kubernetesConfigRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.locator_retry;
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.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.enabled=false",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class ConfigRetryDisabledButSecretsRetryEnabled {
private static final String API = "/api/v1/namespaces/default/configmaps/application";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
}
@SpyBean
private Fabric8ConfigMapPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
/*
* Enabling secrets retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for config maps to be
* defined. ConfigMapPropertySourceLocator should not retry even Spring Retry is
* enabled.
*/
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
assertThat(context.containsBean("kubernetesConfigRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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.locator_retry;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
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.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.config.retry.max-attempts=5",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class ConfigRetryEnabled {
private static final String API = "/api/v1/namespaces/default/configmaps/application";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
}
@SpyBean
private Fabric8ConfigMapPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, String> data = new HashMap<>();
data.put("some.prop", "theValue");
data.put("some.number", "0");
// return config map without failing
mockServer.expect().withPath(API).andReturn(200,
new ConfigMapBuilder().withNewMetadata().withName("application").endMetadata().addToData(data).build())
.once();
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.prop")).isEqualTo("theValue");
assertThat(propertySource.getProperty("some.number")).isEqualTo("0");
}
@Test
void locateShouldRetryAndRecover() {
Map<String, String> data = new HashMap<>();
data.put("some.prop", "theValue");
data.put("some.number", "0");
// fail 3 times then succeed at the 4th call
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").times(3);
mockServer.expect().withPath(API).andReturn(200,
new ConfigMapBuilder().withNewMetadata().withName("application").endMetadata().addToData(data).build())
.once();
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.prop")).isEqualTo("theValue");
assertThat(propertySource.getProperty("some.number")).isEqualTo("0");
}
@Test
void locateShouldRetryAndFail() {
// fail all the 5 requests
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").times(5);
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read ConfigMap with name 'application' in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.retry;
import io.fabric8.kubernetes.api.model.SecretListBuilder;
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.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.name=my-secret", "spring.cloud.kubernetes.secrets.enable-api=true",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsFailFastDisabled {
private static final String API = "/api/v1/namespaces/default/secrets/my-secret";
private static final String LIST_API = "/api/v1/namespaces/default/secrets";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
// return empty secret list to not fail context creation
mockServer.expect().withPath(LIST_API).andReturn(200, new SecretListBuilder().build()).always();
}
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetry() {
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
Assertions.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.retry;
import io.fabric8.kubernetes.api.model.SecretListBuilder;
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.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.cloud.kubernetes.secrets.retry.enabled=false",
"spring.cloud.kubernetes.secrets.name=my-secret", "spring.cloud.kubernetes.secrets.enable-api=true",
"spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsFailFastEnabledButRetryDisabled {
private static final String API = "/api/v1/namespaces/default/secrets/my-secret";
private static final String LIST_API = "/api/v1/namespaces/default/secrets";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
// return empty secret list to not fail context creation
mockServer.expect().withPath(LIST_API).andReturn(200, new SecretListBuilder().build()).always();
}
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isFalse();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.retry;
import io.fabric8.kubernetes.api.model.SecretListBuilder;
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.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = { "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.kubernetes.secrets.fail-fast=true", "spring.cloud.kubernetes.secrets.retry.enabled=false",
"spring.cloud.kubernetes.config.fail-fast=true", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsRetryDisabledButConfigRetryEnabled {
private static final String API = "/api/v1/namespaces/default/secrets/my-secret";
private static final String LIST_API = "/api/v1/namespaces/default/secrets";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
// return empty secret list to not fail context creation
mockServer.expect().withPath(LIST_API).andReturn(200, new SecretListBuilder().build()).always();
}
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Autowired
private ApplicationContext context;
@Test
void locateShouldFailWithoutRetrying() {
/*
* Enabling config retry causes Spring Retry to be enabled and a
* RetryOperationsInterceptor bean with NeverRetryPolicy for secrets to be
* defined. SecretsPropertySourceLocator should not retry even Spring Retry is
* enabled.
*/
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").once();
assertThat(context.containsBean("kubernetesSecretsRetryInterceptor")).isTrue();
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify that propertySourceLocator.locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.retry;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.SecretBuilder;
import io.fabric8.kubernetes.api.model.SecretListBuilder;
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.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.kubernetes.fabric8.config.Application;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Isik Erhan
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, properties = {
"spring.cloud.kubernetes.client.namespace=default", "spring.cloud.kubernetes.secrets.fail-fast=true",
"spring.cloud.kubernetes.secrets.retry.max-attempts=5", "spring.cloud.kubernetes.secrets.name=my-secret",
"spring.cloud.kubernetes.secrets.enable-api=true", "spring.main.cloud-platform=KUBERNETES" },
classes = Application.class)
@EnableKubernetesMockClient
class SecretsRetryEnabled {
private static final String API = "/api/v1/namespaces/default/secrets/my-secret";
private static final String LIST_API = "/api/v1/namespaces/default/secrets";
private static KubernetesMockServer mockServer;
private static KubernetesClient mockClient;
@BeforeAll
static void setup() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true");
System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
// return empty secret list to not fail context creation
mockServer.expect().withPath(LIST_API).andReturn(200, new SecretListBuilder().build()).always();
}
@SpyBean
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Test
void locateShouldNotRetryWhenThereIsNoFailure() {
Map<String, String> data = new HashMap<>();
data.put("some.sensitive.prop", Base64.getEncoder().encodeToString("theSensitiveValue".getBytes()));
data.put("some.sensitive.number", Base64.getEncoder().encodeToString("1".getBytes()));
// return secret without failing
mockServer.expect().withPath(API).andReturn(200,
new SecretBuilder().withNewMetadata().withName("my-secret").endMetadata().addToData(data).build())
.once();
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify locate is called only once
verify(propertySourceLocator, times(1)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
void locateShouldRetryAndRecover() {
Map<String, String> data = new HashMap<>();
data.put("some.sensitive.prop", Base64.getEncoder().encodeToString("theSensitiveValue".getBytes()));
data.put("some.sensitive.number", Base64.getEncoder().encodeToString("1".getBytes()));
// fail 3 times then succeed at the 4th call
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").times(3);
mockServer.expect().withPath(API).andReturn(200,
new SecretBuilder().withNewMetadata().withName("my-secret").endMetadata().addToData(data).build())
.once();
PropertySource<?> propertySource = Assertions
.assertDoesNotThrow(() -> propertySourceLocator.locate(new MockEnvironment()));
// verify retried 4 times
verify(propertySourceLocator, times(4)).locate(any());
// validate the contents of the property source
assertThat(propertySource.getProperty("some.sensitive.prop")).isEqualTo("theSensitiveValue");
assertThat(propertySource.getProperty("some.sensitive.number")).isEqualTo("1");
}
@Test
void locateShouldRetryAndFail() {
// fail all the 5 requests
mockServer.expect().withPath(API).andReturn(500, "Internal Server Error").times(5);
assertThatThrownBy(() -> propertySourceLocator.locate(new MockEnvironment()))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Unable to read Secret with name 'my-secret' or labels [{}] in namespace 'default'");
// verify retried 5 times until failure
verify(propertySourceLocator, times(5)).locate(any());
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.istio;
import me.snowdrop.istio.client.IstioClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "spring.cloud.istio.enabled=false" })
class IstioAutoConfigurationClientNotPresentWhenIstioDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void istioClientNotPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.istio;
import me.snowdrop.istio.client.IstioClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "spring.cloud.kubernetes.enabled=false" })
class IstioAutoConfigurationClientNotPresentWhenKubernetesDisabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void istioClientNotPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(0);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.istio;
import me.snowdrop.istio.client.IstioClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = "spring.main.cloud-platform=KUBERNETES")
class IstioAutoConfigurationClientPresentByDefault {
@Autowired
private ConfigurableApplicationContext context;
@Test
void istioClientIsPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(1);
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.istio;
import me.snowdrop.istio.client.IstioClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "spring.cloud.istio.enabled=true", "spring.main.cloud-platform=KUBERNETES" })
class IstioAutoConfigurationClientPresentWhenIstioEnabled {
@Autowired
private ConfigurableApplicationContext context;
@Test
void istioClientIsPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(1);
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2013-2021 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.istio;
import me.snowdrop.istio.client.IstioClient;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author wind57
*/
public class IstioAutoConfigurationTests {
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = "spring.main.cloud-platform=KUBERNETES")
@Nested
class IstioClientPresentByDefault {
@Autowired
ConfigurableApplicationContext context;
@Test
public void istioClientIsPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class)
@Nested
class IstioClientNotPresentWhenKubernetesDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void istioClientNotPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(0);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.istio.enabled=true" })
@Nested
class IstioClientPresentWhenIstioEnabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void istioClientIsPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(1);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
properties = { "spring.cloud.istio.enabled=false" })
@Nested
class IstioClientNotPresentPresentWhenIstioDisabled {
@Autowired
ConfigurableApplicationContext context;
@Test
public void istioClientNotPresent() {
assertThat(context.getBeanNamesForType(IstioClient.class)).hasSize(0);
}
}
}