Kubernetes as a Composite config source (#1873)
* initial commit, crearting factory Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * testing default Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * remove checkstyle Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * for try Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * for try Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * trying existing bean Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * reverting pom.xml Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * ObjectProvider for missing bean Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * Update default order value in KubernetesConfigServerProperties Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * code style fixes Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * code style fixes Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * code style fixes Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * code style Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * test cases Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * Composite Integration Tests where kubernetes is one of the type in the composite setup KubernetesEnvironmentRepository as condition for KubernetesEnvironmentRepositoryFactory bean creation Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * Composite Integration Tests where kubernetes is one of the type in the composite setup KubernetesEnvironmentRepository as condition for KubernetesEnvironmentRepositoryFactory bean creation Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * Add KubernetesConfigServerProperties support to KubernetesEnvironmentRepository and related tests Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * mark old implementation as deprecated and revert test cases Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * test case for new constructor Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * linting Signed-off-by: Arjav <arjavdongaonkar@gmail.com> * remove ObjectProvider Signed-off-by: Arjav <arjavdongaonkar@gmail.com> --------- Signed-off-by: Arjav <arjavdongaonkar@gmail.com>
This commit is contained in:
@@ -23,12 +23,13 @@ import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
|
||||
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
|
||||
@@ -59,11 +60,21 @@ public class KubernetesConfigServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Profile("kubernetes")
|
||||
public EnvironmentRepository kubernetesEnvironmentRepository(CoreV1Api coreV1Api,
|
||||
@ConditionalOnMissingBean
|
||||
public KubernetesEnvironmentRepository kubernetesEnvironmentRepository(CoreV1Api coreV1Api,
|
||||
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers,
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider) {
|
||||
KubernetesNamespaceProvider kubernetesNamespaceProvider,
|
||||
KubernetesConfigServerProperties kubernetesConfigServerProperties) {
|
||||
return new KubernetesEnvironmentRepository(coreV1Api, kubernetesPropertySourceSuppliers,
|
||||
kubernetesNamespaceProvider.getNamespace());
|
||||
kubernetesNamespaceProvider.getNamespace(), kubernetesConfigServerProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(KubernetesEnvironmentRepository.class)
|
||||
@ConditionalOnMissingBean
|
||||
public KubernetesEnvironmentRepositoryFactory kubernetesEnvironmentRepositoryFactory(
|
||||
KubernetesEnvironmentRepository kubernetesEnvironmentRepository) {
|
||||
return new KubernetesEnvironmentRepositoryFactory(kubernetesEnvironmentRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -17,13 +17,16 @@
|
||||
package org.springframework.cloud.kubernetes.configserver;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.config.server.support.EnvironmentRepositoryProperties;
|
||||
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
|
||||
@ConfigurationProperties("spring.cloud.kubernetes.configserver")
|
||||
public class KubernetesConfigServerProperties {
|
||||
public class KubernetesConfigServerProperties implements EnvironmentRepositoryProperties {
|
||||
|
||||
private int order = DEFAULT_ORDER;
|
||||
|
||||
private String configMapNamespaces = "";
|
||||
|
||||
@@ -45,4 +48,12 @@ public class KubernetesConfigServerProperties {
|
||||
this.secretsNamespaces = secretsNamespaces;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.config.environment.Environment;
|
||||
import org.springframework.cloud.config.environment.PropertySource;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
@@ -36,7 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
/**
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
public class KubernetesEnvironmentRepository implements EnvironmentRepository {
|
||||
public class KubernetesEnvironmentRepository implements EnvironmentRepository, Ordered {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(KubernetesEnvironmentRepository.class);
|
||||
|
||||
@@ -46,6 +47,9 @@ public class KubernetesEnvironmentRepository implements EnvironmentRepository {
|
||||
|
||||
private final String namespace;
|
||||
|
||||
private int order = KubernetesConfigServerProperties.DEFAULT_ORDER;
|
||||
|
||||
@Deprecated
|
||||
public KubernetesEnvironmentRepository(CoreV1Api coreApi,
|
||||
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers, String namespace) {
|
||||
this.coreApi = coreApi;
|
||||
@@ -53,6 +57,15 @@ public class KubernetesEnvironmentRepository implements EnvironmentRepository {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public KubernetesEnvironmentRepository(CoreV1Api coreApi,
|
||||
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers, String namespace,
|
||||
KubernetesConfigServerProperties properties) {
|
||||
this.coreApi = coreApi;
|
||||
this.kubernetesPropertySourceSuppliers = kubernetesPropertySourceSuppliers;
|
||||
this.namespace = namespace;
|
||||
this.order = properties.getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Environment findOne(String application, String profile, String label) {
|
||||
return findOne(application, profile, label, true);
|
||||
@@ -118,4 +131,13 @@ public class KubernetesEnvironmentRepository implements EnvironmentRepository {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.springframework.cloud.config.server.environment.EnvironmentRepositoryFactory;
|
||||
|
||||
/**
|
||||
* Factory class for creating instances of {@link KubernetesEnvironmentRepository}.
|
||||
*
|
||||
* @author Arjav Dongaonkar
|
||||
*/
|
||||
public class KubernetesEnvironmentRepositoryFactory
|
||||
implements EnvironmentRepositoryFactory<KubernetesEnvironmentRepository, KubernetesConfigServerProperties> {
|
||||
|
||||
private final KubernetesEnvironmentRepository kubernetesEnvironmentRepository;
|
||||
|
||||
public KubernetesEnvironmentRepositoryFactory(KubernetesEnvironmentRepository kubernetesEnvironmentRepository) {
|
||||
this.kubernetesEnvironmentRepository = kubernetesEnvironmentRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KubernetesEnvironmentRepository build(KubernetesConfigServerProperties environmentProperties) {
|
||||
return kubernetesEnvironmentRepository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.configserver;
|
||||
|
||||
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.JGitEnvironmentRepository;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjav Dongaonkar
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class },
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes",
|
||||
"spring.cloud.kubernetes.client.namespace=default", "spring.profiles.active=composite",
|
||||
"spring.cloud.config.server.composite[0].type=git",
|
||||
"spring.cloud.config.server.composite[0].uri=https://github.com/spring-cloud-samples/config-repo",
|
||||
"spring.cloud.config.server.composite[1].type=kubernetes",
|
||||
"spring.cloud.config.server.composite[1].config-map-namespace=default",
|
||||
"spring.cloud.config.server.composite[1].secrets-namespace=default" })
|
||||
class CompositeProfileWithGitAndKubernetesConfigSourcesTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(2);
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepositoryFactory.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).isNotEmpty();
|
||||
assertThat(context.getBeanNamesForType(JGitEnvironmentRepository.class)).hasSize(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 Arjav Dongaonkar
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class },
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes",
|
||||
"spring.cloud.kubernetes.client.namespace=default", "spring.profiles.active=composite",
|
||||
"spring.cloud.config.server.composite[0].type=kubernetes",
|
||||
"spring.cloud.config.server.composite[0].config-map-namespace=default",
|
||||
"spring.cloud.config.server.composite[0].secrets-namespace=default",
|
||||
"spring.cloud.config.server.composite[1].type=kubernetes",
|
||||
"spring.cloud.config.server.composite[1].config-map-namespace=another-namespace",
|
||||
"spring.cloud.config.server.composite[1].secrets-namespace=another-namespace" })
|
||||
class CompositeProfileWithMultipleKubernetesConfigSourcesTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(3);
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepositoryFactory.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 Arjav Dongaonkar
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = { KubernetesConfigServerApplication.class },
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.profiles.include=kubernetes",
|
||||
"spring.cloud.kubernetes.client.namespace=default", "spring.profiles.active=composite",
|
||||
"spring.cloud.config.server.composite[0].type=kubernetes",
|
||||
"spring.cloud.config.server.composite[0].config-map-namespace=default",
|
||||
"spring.cloud.config.server.composite[0].secrets-namespace=default" })
|
||||
class CompositeProfileWithOnlyKubernetesConfigSourceTests {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(2);
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepositoryFactory.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,6 +38,7 @@ class ConfigServerAutoConfigurationKubernetesDisabledTests {
|
||||
@Test
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(0);
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepositoryFactory.class)).hasSize(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class ConfigServerAutoConfigurationKubernetesEnabledProfileIncludedConfigApiDisa
|
||||
void runTest() {
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepository.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(KubernetesPropertySourceSupplier.class)).hasSize(0);
|
||||
assertThat(context.getBeanNamesForType(KubernetesEnvironmentRepositoryFactory.class)).hasSize(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@SpringJUnitConfig
|
||||
@SpringBootTest
|
||||
class KubernetesEnvironmentRepositoryFactoryTests {
|
||||
|
||||
@MockBean
|
||||
private KubernetesEnvironmentRepository mockRepository;
|
||||
|
||||
@Test
|
||||
void testBuild() {
|
||||
KubernetesEnvironmentRepositoryFactory factory = new KubernetesEnvironmentRepositoryFactory(mockRepository);
|
||||
KubernetesConfigServerProperties properties = new KubernetesConfigServerProperties();
|
||||
|
||||
EnvironmentRepository repository = factory.build(properties);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
assertThat(repository).isInstanceOf(KubernetesEnvironmentRepository.class);
|
||||
assertThat(repository).isSameAs(mockRepository);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public KubernetesEnvironmentRepository kubernetesEnvironmentRepository() {
|
||||
return mock(KubernetesEnvironmentRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KubernetesEnvironmentRepositoryFactory kubernetesEnvironmentRepositoryFactory(
|
||||
KubernetesEnvironmentRepository kubernetesEnvironmentRepository) {
|
||||
return new KubernetesEnvironmentRepositoryFactory(kubernetesEnvironmentRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -190,6 +190,48 @@ class KubernetesEnvironmentRepositoryTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplicationCaseWithNewConstructor() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
KubernetesConfigServerProperties properties = mock(KubernetesConfigServerProperties.class);
|
||||
when(properties.getOrder()).thenReturn(0);
|
||||
|
||||
when(coreApi.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)))
|
||||
.thenReturn(CONFIGMAP_DEFAULT_LIST);
|
||||
when(coreApi.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)))
|
||||
.thenReturn(SECRET_LIST);
|
||||
when(coreApi.listNamespacedConfigMap(eq("dev"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
|
||||
eq(null), eq(null), eq(null), eq(null), eq(null)))
|
||||
.thenReturn(CONFIGMAP_DEV_LIST);
|
||||
|
||||
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
|
||||
KUBERNETES_PROPERTY_SOURCE_SUPPLIER, "default", properties);
|
||||
|
||||
Environment environment = environmentRepository.findOne("application", "", "");
|
||||
|
||||
assertThat(environment.getPropertySources().size()).isEqualTo(2);
|
||||
environment.getPropertySources().forEach(propertySource -> {
|
||||
assertThat(propertySource.getName().equals("configmap.application.default")
|
||||
|| propertySource.getName().equals("secret.application.default"))
|
||||
.isTrue();
|
||||
if (propertySource.getName().equals("configmap.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(3);
|
||||
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(1);
|
||||
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(true);
|
||||
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("a");
|
||||
}
|
||||
if (propertySource.getName().equals("secrets.application.default")) {
|
||||
assertThat(propertySource.getSource().size()).isEqualTo(2);
|
||||
assertThat(propertySource.getSource().get("username")).isEqualTo("user");
|
||||
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(environmentRepository.getOrder()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresCase() throws ApiException {
|
||||
CoreV1Api coreApi = mock(CoreV1Api.class);
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
* 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.it;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.kubernetes.client.openapi.ApiException;
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMapList;
|
||||
import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
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.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||
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;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Arjav Dongaonkar
|
||||
*/
|
||||
public class CompositeKubernetesIntegrationTests {
|
||||
|
||||
private static final List<KubernetesPropertySourceSupplier> KUBERNETES_PROPERTY_SOURCE_SUPPLIER = new ArrayList<>();
|
||||
|
||||
private static V1ConfigMap buildConfigMap(String name, String namespace) {
|
||||
return new V1ConfigMapBuilder()
|
||||
.withMetadata(
|
||||
new V1ObjectMetaBuilder().withName(name).withNamespace(namespace).withResourceVersion("1").build())
|
||||
.addToData(Constants.APPLICATION_YAML, "dummy:\n property:\n string: \"" + name + "\"\n")
|
||||
.build();
|
||||
}
|
||||
|
||||
private static V1Secret buildSecret(String name, String namespace) {
|
||||
return new V1SecretBuilder()
|
||||
.withMetadata(
|
||||
new V1ObjectMetaBuilder().withName(name).withResourceVersion("0").withNamespace(namespace).build())
|
||||
.addToData("password", "p455w0rd".getBytes())
|
||||
.addToData("username", "user".getBytes())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
|
||||
.addItemsItem(buildConfigMap("gateway", "default"));
|
||||
|
||||
private static final V1SecretList SECRET_DEFAULT_LIST = new V1SecretListBuilder()
|
||||
.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() {
|
||||
new KubernetesClientConfigMapsCache().discardAll();
|
||||
}
|
||||
|
||||
@Nested
|
||||
@SpringBootTest(classes = { KubernetesConfigServerApplication.class },
|
||||
properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.client.namespace=default",
|
||||
"spring.config.name=compositeconfigserver",
|
||||
"spring.cloud.config.server.composite[0].type=kubernetes",
|
||||
"spring.cloud.kubernetes.secrets.enableApi=true" },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles({ "test", "composite", "kubernetes" })
|
||||
class KubernetesCompositeConfigServerTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@MockBean
|
||||
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)))
|
||||
.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)))
|
||||
.thenReturn(SECRET_DEFAULT_LIST);
|
||||
|
||||
ResponseEntity<Environment> response = new RestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/gateway/default", HttpMethod.GET, null, Environment.class);
|
||||
|
||||
Environment environment = response.getBody();
|
||||
assertThat(environment).isNotNull();
|
||||
assertThat(environment.getPropertySources()).hasSize(4);
|
||||
assertThat(environment.getPropertySources().get(0).getName())
|
||||
.isEqualTo("configmap.gateway.default.default");
|
||||
assertThat(environment.getPropertySources().get(1).getName()).contains("secret.gateway.default.default");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@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.kubernetes.secrets.enableApi=true" },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles({ "test", "composite", "kubernetes", "native" })
|
||||
class KubernetesSecretsEnabledCompositeConfigServerTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@MockBean
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@SpyBean
|
||||
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)))
|
||||
.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)))
|
||||
.thenReturn(SECRET_DEFAULT_LIST);
|
||||
|
||||
Environment mockNativeEnvironment = new Environment("gateway", "default");
|
||||
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
|
||||
|
||||
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
|
||||
.thenReturn(mockNativeEnvironment);
|
||||
|
||||
ResponseEntity<Environment> response = new RestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/gateway/default", HttpMethod.GET, null, Environment.class);
|
||||
|
||||
Environment environment = response.getBody();
|
||||
assertThat(environment).isNotNull();
|
||||
assertThat(environment.getPropertySources()).hasSizeGreaterThanOrEqualTo(5);
|
||||
|
||||
assertThat(environment.getPropertySources().get(0).getName())
|
||||
.isEqualTo("configmap.gateway.default.default");
|
||||
assertThat(environment.getPropertySources().get(1).getName()).contains("secret.gateway.default.default");
|
||||
|
||||
assertThat(environment.getPropertySources()).anyMatch(ps -> ps.getName().contains("native"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@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.kubernetes.config.enableApi=false",
|
||||
"spring.cloud.kubernetes.secrets.enableApi=true" },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles({ "test", "composite", "kubernetes", "native" })
|
||||
class KubernetesConfigMapDisabledCompositeConfigServerTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@MockBean
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@SpyBean
|
||||
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)))
|
||||
.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)))
|
||||
.thenReturn(SECRET_DEFAULT_LIST);
|
||||
|
||||
Environment mockNativeEnvironment = new Environment("gateway", "default");
|
||||
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
|
||||
|
||||
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
|
||||
.thenReturn(mockNativeEnvironment);
|
||||
|
||||
ResponseEntity<Environment> response = new RestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/gateway/default", HttpMethod.GET, null, Environment.class);
|
||||
|
||||
Environment environment = response.getBody();
|
||||
assertThat(environment).isNotNull();
|
||||
assertThat(environment.getPropertySources()).hasSizeGreaterThanOrEqualTo(3);
|
||||
|
||||
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("secret.gateway.default.default");
|
||||
assertThat(environment.getPropertySources().get(1).getName()).contains("nativeProperties");
|
||||
|
||||
assertThat(environment.getPropertySources()).anyMatch(ps -> ps.getName().contains("native"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@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" },
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@ActiveProfiles({ "test", "composite", "kubernetes", "native" })
|
||||
class KubernetesSecretsDisabledCompositeConfigServerTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@MockBean
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@SpyBean
|
||||
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)))
|
||||
.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)))
|
||||
.thenReturn(SECRET_DEFAULT_LIST);
|
||||
|
||||
Environment mockNativeEnvironment = new Environment("gateway", "default");
|
||||
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
|
||||
|
||||
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
|
||||
.thenReturn(mockNativeEnvironment);
|
||||
|
||||
ResponseEntity<Environment> response = new RestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/gateway/default", HttpMethod.GET, null, Environment.class);
|
||||
|
||||
Environment environment = response.getBody();
|
||||
assertThat(environment).isNotNull();
|
||||
assertThat(environment.getPropertySources()).hasSizeGreaterThanOrEqualTo(3);
|
||||
|
||||
assertThat(environment.getPropertySources().get(0).getName())
|
||||
.isEqualTo("configmap.gateway.default.default");
|
||||
assertThat(environment.getPropertySources().get(1).getName()).contains("nativeProperties");
|
||||
|
||||
assertThat(environment.getPropertySources()).anyMatch(ps -> ps.getName().contains("native"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
@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)
|
||||
@ActiveProfiles({ "test", "native", "kubernetes" })
|
||||
class NativeAndKubernetesConfigServerTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
@MockBean
|
||||
private CoreV1Api coreV1Api;
|
||||
|
||||
@SpyBean
|
||||
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)))
|
||||
.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)))
|
||||
.thenReturn(SECRET_DEFAULT_LIST);
|
||||
|
||||
Environment mockNativeEnvironment = new Environment("gateway", "default");
|
||||
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
|
||||
|
||||
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
|
||||
.thenReturn(mockNativeEnvironment);
|
||||
|
||||
ResponseEntity<Environment> response = new RestTemplate().exchange(
|
||||
"http://localhost:" + this.port + "/gateway/default", HttpMethod.GET, null, Environment.class);
|
||||
|
||||
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")
|
||||
&& !environment.getPropertySources().get(1).getName().contains("nativeProperties"))
|
||||
.isTrue();
|
||||
assertThat(environment.getPropertySources().get(2).getName()).contains("secret.gateway.default.default");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user