Signed-off-by: wind57 <eugen.rabii@gmail.com>
This commit is contained in:
wind57
2025-04-30 13:40:05 +03:00
parent 09c00d06a6
commit f1f94df17c
3 changed files with 101 additions and 94 deletions

View File

@@ -16,8 +16,6 @@
package org.springframework.cloud.kubernetes.configserver.it;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import io.kubernetes.client.openapi.ApiException;
@@ -31,10 +29,10 @@ import io.kubernetes.client.openapi.models.V1SecretBuilder;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
@@ -42,15 +40,9 @@ import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepository;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapsCache;
import org.springframework.cloud.kubernetes.commons.config.Constants;
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
import org.springframework.cloud.kubernetes.configserver.KubernetesConfigServerApplication;
import org.springframework.cloud.kubernetes.configserver.KubernetesPropertySourceSupplier;
import org.springframework.core.env.MapPropertySource;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ActiveProfiles;
@@ -65,14 +57,12 @@ import static org.mockito.Mockito.when;
/**
* @author Arjav Dongaonkar
*/
public class CompositeKubernetesIntegrationTests {
private static final List<KubernetesPropertySourceSupplier> KUBERNETES_PROPERTY_SOURCE_SUPPLIER = new ArrayList<>();
class CompositeKubernetesIntegrationTests {
private static V1ConfigMap buildConfigMap(String name, String namespace) {
return new V1ConfigMapBuilder()
.withMetadata(
new V1ObjectMetaBuilder().withName(name).withNamespace(namespace).withResourceVersion("1").build())
new V1ObjectMetaBuilder().withName(name).withNamespace(namespace).build())
.addToData(Constants.APPLICATION_YAML, "dummy:\n property:\n string: \"" + name + "\"\n")
.build();
}
@@ -80,7 +70,7 @@ public class CompositeKubernetesIntegrationTests {
private static V1Secret buildSecret(String name, String namespace) {
return new V1SecretBuilder()
.withMetadata(
new V1ObjectMetaBuilder().withName(name).withResourceVersion("0").withNamespace(namespace).build())
new V1ObjectMetaBuilder().withName(name).withNamespace(namespace).build())
.addToData("password", "p455w0rd".getBytes())
.addToData("username", "user".getBytes())
.build();
@@ -93,22 +83,8 @@ public class CompositeKubernetesIntegrationTests {
.addToItems(buildSecret("gateway", "default"))
.build();
@BeforeAll
public static void before() {
KUBERNETES_PROPERTY_SOURCE_SUPPLIER.add((coreApi, applicationName, namespace, springEnv) -> {
List<MapPropertySource> propertySources = new ArrayList<>();
NormalizedSource defaultSource = new NamedConfigMapNormalizedSource(applicationName, "default", false,
true);
KubernetesClientConfigContext defaultContext = new KubernetesClientConfigContext(coreApi, defaultSource,
"default", springEnv);
propertySources.add(new KubernetesClientConfigMapPropertySource(defaultContext));
return propertySources;
});
}
@AfterEach
public void after() {
void after() {
new KubernetesClientConfigMapsCache().discardAll();
}
@@ -129,12 +105,12 @@ public class CompositeKubernetesIntegrationTests {
private CoreV1Api coreV1Api;
@Test
public void contextLoads() throws ApiException {
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
void contextLoads() throws ApiException {
when(coreV1Api.listNamespacedConfigMap(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(CONFIGMAP_DEFAULT_LIST);
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
when(coreV1Api.listNamespacedSecret(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(SECRET_DEFAULT_LIST);
ResponseEntity<Environment> response = new RestTemplate().exchange(
@@ -155,7 +131,6 @@ public class CompositeKubernetesIntegrationTests {
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.config.server.composite[0].type=kubernetes",
"spring.cloud.config.server.composite[1].type=native",
"spring.cloud.config.server.composite[1].location=file:./native-config",
"spring.cloud.kubernetes.secrets.enableApi=true" },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({ "test", "composite", "kubernetes", "native" })
@@ -171,12 +146,12 @@ public class CompositeKubernetesIntegrationTests {
private NativeEnvironmentRepository nativeEnvironmentRepository;
@Test
public void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(CONFIGMAP_DEFAULT_LIST);
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
when(coreV1Api.listNamespacedSecret(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(SECRET_DEFAULT_LIST);
Environment mockNativeEnvironment = new Environment("gateway", "default");
@@ -206,7 +181,6 @@ public class CompositeKubernetesIntegrationTests {
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.config.server.composite[0].type=kubernetes",
"spring.cloud.config.server.composite[1].type=native",
"spring.cloud.config.server.composite[1].location=file:./native-config",
"spring.cloud.kubernetes.config.enableApi=false",
"spring.cloud.kubernetes.secrets.enableApi=true" },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@@ -223,12 +197,12 @@ public class CompositeKubernetesIntegrationTests {
private NativeEnvironmentRepository nativeEnvironmentRepository;
@Test
public void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(CONFIGMAP_DEFAULT_LIST);
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
when(coreV1Api.listNamespacedSecret(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(SECRET_DEFAULT_LIST);
Environment mockNativeEnvironment = new Environment("gateway", "default");
@@ -256,8 +230,7 @@ public class CompositeKubernetesIntegrationTests {
@SpringBootTest(classes = { KubernetesConfigServerApplication.class },
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.config.server.composite[0].type=kubernetes",
"spring.cloud.config.server.composite[1].type=native",
"spring.cloud.config.server.composite[1].location=file:./native-config" },
"spring.cloud.config.server.composite[1].type=native" },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({ "test", "composite", "kubernetes", "native" })
class KubernetesSecretsDisabledCompositeConfigServerTest {
@@ -272,12 +245,12 @@ public class CompositeKubernetesIntegrationTests {
private NativeEnvironmentRepository nativeEnvironmentRepository;
@Test
public void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(CONFIGMAP_DEFAULT_LIST);
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
when(coreV1Api.listNamespacedSecret(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(SECRET_DEFAULT_LIST);
Environment mockNativeEnvironment = new Environment("gateway", "default");
@@ -306,7 +279,6 @@ public class CompositeKubernetesIntegrationTests {
@SpringBootTest(classes = { KubernetesConfigServerApplication.class },
properties = { "spring.config.name:compositeconfigserver", "spring.main.cloud-platform=KUBERNETES",
"spring.cloud.kubernetes.client.namespace=default",
"spring.cloud.config.server.native.search-locations=file:./native-config",
"spring.cloud.config.server.native.order=1", "spring.cloud.kubernetes.configserver.order=2",
"spring.cloud.kubernetes.secrets.enableApi=true" },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@@ -323,12 +295,12 @@ public class CompositeKubernetesIntegrationTests {
private NativeEnvironmentRepository nativeEnvironmentRepository;
@Test
public void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
void contextLoads() throws Exception {
when(coreV1Api.listNamespacedConfigMap(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(CONFIGMAP_DEFAULT_LIST);
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null),
eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)))
when(coreV1Api.listNamespacedSecret(
"default", null, null, null, null, null, null, null, null, null, null, null))
.thenReturn(SECRET_DEFAULT_LIST);
Environment mockNativeEnvironment = new Environment("gateway", "default");
@@ -340,9 +312,14 @@ public class CompositeKubernetesIntegrationTests {
ResponseEntity<Environment> response = new RestTemplate().exchange(
"http://localhost:" + this.port + "/gateway/default", HttpMethod.GET, null, Environment.class);
Mockito.verify(coreV1Api, Mockito.times(1)).listNamespacedConfigMap(
"default", null, null, null, null, null, null, null, null, null, null, null);
Mockito.verify(coreV1Api, Mockito.times(1)).listNamespacedSecret(
"default", null, null, null, null, null, null, null, null, null, null, null);
Environment environment = response.getBody();
assert environment != null;
assertThat(3).isEqualTo(environment.getPropertySources().size());
assertThat("nativeProperties").isEqualTo(environment.getPropertySources().get(0).getName());
assertThat(environment.getPropertySources().get(1).getName().contains("configmap.gateway.default.default")

View File

@@ -17,6 +17,7 @@
package org.springframework.cloud.kubernetes.configserver.it;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -43,17 +44,13 @@ class ConfigDataConfigServerIntegrationTests extends ConfigServerIntegration {
@BeforeEach
void setup() {
clientUtilsMock = mockStatic(KubernetesClientUtils.class);
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
.thenReturn(new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build());
ApiClient client = new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build();
clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient).thenReturn(client);
}
@AfterEach
void teardown() {
clientUtilsMock.close();
}
@AfterEach
void afterEach() {
WireMock.reset();
}

View File

@@ -26,6 +26,7 @@ import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
import io.kubernetes.client.openapi.models.V1SecretBuilder;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -46,6 +47,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
abstract class ConfigServerIntegration {
private static final String SOURCE_NAME = "test-cm";
private static final String NAMESPACE = "default";
private static final String TEST_CONFIG_MAP_DEV_YAML = "test-cm-dev.yaml";
private static final String TEST_CONFIG_MAP_DEV_NAME = "configmap.test-cm.default.dev";
private static final String TEST_CONFIG_MAP_DEV_DATA = """
@@ -85,6 +90,8 @@ abstract class ConfigServerIntegration {
enabled: true
""";
private static final String TEST_SECRET_NAME = "secret.test-cm.default.default";
@Autowired
private TestRestTemplate testRestTemplate;
@@ -94,7 +101,7 @@ abstract class ConfigServerIntegration {
@BeforeEach
void beforeEach() {
V1ConfigMapList TEST_CONFIGMAP = new V1ConfigMapList().addItemsItem(new V1ConfigMapBuilder().withMetadata(
new V1ObjectMetaBuilder().withName("test-cm").withNamespace("default").build())
new V1ObjectMetaBuilder().withName(SOURCE_NAME).withNamespace(NAMESPACE).build())
.addToData(TEST_CONFIG_MAP_DEV_YAML, TEST_CONFIG_MAP_DEV_DATA)
.addToData(TEST_CONFIG_MAP_QA_YAML, TEST_CONFIG_MAP_QA_DATA)
.addToData(TEST_CONFIG_MAP_PROD_YAML, TEST_CONFIG_MAP_PROD_DATA)
@@ -105,9 +112,8 @@ abstract class ConfigServerIntegration {
V1SecretList TEST_SECRET = new V1SecretListBuilder()
.withMetadata(new V1ListMetaBuilder().build())
.addToItems(new V1SecretBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("test-cm")
.withResourceVersion("0")
.withNamespace("default")
.withMetadata(new V1ObjectMetaBuilder().withName(SOURCE_NAME)
.withNamespace(NAMESPACE)
.build())
.addToData("password", "p455w0rd".getBytes())
.addToData("username", "user".getBytes())
@@ -121,28 +127,25 @@ abstract class ConfigServerIntegration {
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(TEST_SECRET))));
}
@AfterEach
void afterEach() {
WireMock.reset();
WireMock.shutdownServer();
}
@Test
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.default")).isTrue();
assertThat(env.getPropertySources().get(0).getSource().get("app.name")).isEqualTo("test");
assertThat(env.getPropertySources().get(1).getName().equals("secret.test-cm.default.default")).isTrue();
assertThat(env.getPropertySources().get(1).getSource().get("password")).isEqualTo("p455w0rd");
assertThat(env.getPropertySources().get(1).getSource().get("username")).isEqualTo("user");
Environment defaultEnv = testRestTemplate.getForObject("/test-cm/default", Environment.class);
assertDefaultProfile(defaultEnv);
Environment devprod = testRestTemplate.getForObject("/test-cm/dev,prod", Environment.class);
assertThat(devprod.getPropertySources().size()).isEqualTo(4);
Environment devAndProd = testRestTemplate.getForObject("/test-cm/dev,prod", Environment.class);
assertThat(devAndProd.getPropertySources().size()).isEqualTo(4);
assertTestConfigMapProd(devprod);
assertTestConfigMapDev(devprod);
assertTestConfigMapDefault(devprod);
assertTestConfigMapProd(devAndProd);
assertTestConfigMapDev(devAndProd);
assertTestConfigMapDefault(devAndProd);
assertTestSecretDefault(devAndProd);
assertThat(devprod.getPropertySources().get(3).getName().equals("secret.test-cm.default.default")).isTrue();
assertThat(devprod.getPropertySources().get(3).getSource().size()).isEqualTo(2);
assertThat(devprod.getPropertySources().get(3).getSource().get("password")).isEqualTo("p455w0rd");
assertThat(devprod.getPropertySources().get(3).getSource().get("username")).isEqualTo("user");
}
private void assertTestConfigMapDev(Environment devAndProd) {
@@ -156,23 +159,53 @@ abstract class ConfigServerIntegration {
}
private void assertTestConfigMapProd(Environment devAndProd) {
PropertySource testConfigMapDev = devAndProd.getPropertySources().get(0);
assertThat(testConfigMapDev.getName()).isEqualTo(TEST_CONFIG_MAP_PROD_NAME);
PropertySource testConfigMapProd = devAndProd.getPropertySources().get(0);
assertThat(testConfigMapProd.getName()).isEqualTo(TEST_CONFIG_MAP_PROD_NAME);
@SuppressWarnings("unchecked")
Map<String, Object> data = (Map<String, Object>) testConfigMapDev.getSource();
Map<String, Object> data = (Map<String, Object>) testConfigMapProd.getSource();
assertThat(data).containsExactlyInAnyOrderEntriesOf(
Map.of("dummy.property.value", 3, "dummy.property.enabled", true, "dummy.property.profile", "prod"));
}
private void assertTestConfigMapDefault(Environment devAndProd) {
PropertySource testConfigMapDev = devAndProd.getPropertySources().get(2);
assertThat(testConfigMapDev.getName()).isEqualTo(TEST_CONFIG_MAP_NAME);
PropertySource testConfigMap = devAndProd.getPropertySources().get(2);
assertThat(testConfigMap.getName()).isEqualTo(TEST_CONFIG_MAP_NAME);
@SuppressWarnings("unchecked")
Map<String, Object> data = (Map<String, Object>) testConfigMapDev.getSource();
Map<String, Object> data = (Map<String, Object>) testConfigMap.getSource();
assertThat(data).containsExactlyInAnyOrderEntriesOf(
Map.of("dummy.property.value", 4, "dummy.property.enabled", true, "dummy.property.profile", "default"));
Map.of("dummy.property.value", 4, "dummy.property.enabled", true, "dummy.property.profile", "default",
"app.name", "test"));
}
private void assertTestSecretDefault(Environment devAndProd) {
PropertySource testSecret = devAndProd.getPropertySources().get(3);
assertThat(testSecret.getName()).isEqualTo(TEST_SECRET_NAME);
@SuppressWarnings("unchecked")
Map<String, Object> data = (Map<String, Object>) testSecret.getSource();
assertThat(data).containsExactlyInAnyOrderEntriesOf(
Map.of("password", "p455w0rd", "username", "user"));
}
private void assertDefaultProfile(Environment defaultEnv) {
assertThat(defaultEnv.getPropertySources().size()).isEqualTo(2);
PropertySource configMapSource = defaultEnv.getPropertySources().get(0);
assertThat(configMapSource.getName()).isEqualTo(TEST_CONFIG_MAP_NAME);
@SuppressWarnings("unchecked")
Map<String, Object> configmapData = (Map<String, Object>) configMapSource.getSource();
assertThat(configmapData).containsExactlyInAnyOrderEntriesOf(
Map.of("dummy.property.value", 4, "dummy.property.enabled", true, "dummy.property.profile", "default",
"app.name", "test"));
PropertySource secretSource = defaultEnv.getPropertySources().get(1);
assertThat(secretSource.getName()).isEqualTo(TEST_SECRET_NAME);
@SuppressWarnings("unchecked")
Map<String, Object> secretData = (Map<String, Object>) secretSource.getSource();
assertThat(secretData).containsExactlyInAnyOrderEntriesOf(Map.of("password", "p455w0rd", "username", "user"));
}
}