Skip to content

Commit ae44c02

Browse files
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 <[email protected]>
1 parent 94d1354 commit ae44c02

File tree

2 files changed

+350
-9
lines changed

2 files changed

+350
-9
lines changed

spring-cloud-kubernetes-controllers/spring-cloud-kubernetes-configserver/src/main/java/org/springframework/cloud/kubernetes/configserver/KubernetesConfigServerAutoConfiguration.java

+10-9
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424
import org.springframework.beans.factory.ObjectProvider;
2525
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2626
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2728
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
2829
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2930
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3031
import org.springframework.boot.cloud.CloudPlatform;
3132
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3233
import org.springframework.cloud.config.server.config.ConfigServerAutoConfiguration;
33-
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
3434
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
3535
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
3636
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
@@ -59,23 +59,24 @@
5959
@EnableConfigurationProperties(KubernetesConfigServerProperties.class)
6060
public class KubernetesConfigServerAutoConfiguration {
6161

62-
@Bean
63-
@ConditionalOnMissingBean
64-
public KubernetesEnvironmentRepositoryFactory kubernetesEnvironmentRepositoryFactory(
65-
ObjectProvider<KubernetesEnvironmentRepository> kubernetesEnvironmentRepositoryProvider) {
66-
return new KubernetesEnvironmentRepositoryFactory(kubernetesEnvironmentRepositoryProvider);
67-
}
68-
6962
@Bean
7063
@Profile("kubernetes")
7164
@ConditionalOnMissingBean
72-
public EnvironmentRepository kubernetesEnvironmentRepository(CoreV1Api coreV1Api,
65+
public KubernetesEnvironmentRepository kubernetesEnvironmentRepository(CoreV1Api coreV1Api,
7366
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers,
7467
KubernetesNamespaceProvider kubernetesNamespaceProvider) {
7568
return new KubernetesEnvironmentRepository(coreV1Api, kubernetesPropertySourceSuppliers,
7669
kubernetesNamespaceProvider.getNamespace());
7770
}
7871

72+
@Bean
73+
@ConditionalOnBean(KubernetesEnvironmentRepository.class)
74+
@ConditionalOnMissingBean
75+
public KubernetesEnvironmentRepositoryFactory kubernetesEnvironmentRepositoryFactory(
76+
ObjectProvider<KubernetesEnvironmentRepository> kubernetesEnvironmentRepositoryProvider) {
77+
return new KubernetesEnvironmentRepositoryFactory(kubernetesEnvironmentRepositoryProvider);
78+
}
79+
7980
@Bean
8081
@ConditionalOnKubernetesConfigEnabled
8182
@ConditionalOnProperty(value = "spring.cloud.kubernetes.config.enableApi", matchIfMissing = true)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
package org.springframework.cloud.kubernetes.configserver.it;
2+
3+
import io.kubernetes.client.openapi.ApiException;
4+
import io.kubernetes.client.openapi.apis.CoreV1Api;
5+
import io.kubernetes.client.openapi.models.*;
6+
import org.junit.jupiter.api.BeforeAll;
7+
import org.junit.jupiter.api.Nested;
8+
import org.junit.jupiter.api.Test;
9+
import org.springframework.boot.test.context.SpringBootTest;
10+
import org.springframework.boot.test.context.TestConfiguration;
11+
import org.springframework.boot.test.mock.mockito.MockBean;
12+
import org.springframework.boot.test.mock.mockito.SpyBean;
13+
import org.springframework.boot.test.web.server.LocalServerPort;
14+
import org.springframework.cloud.config.environment.Environment;
15+
import org.springframework.cloud.config.environment.PropertySource;
16+
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepository;
17+
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigContext;
18+
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
19+
import org.springframework.cloud.kubernetes.commons.config.Constants;
20+
import org.springframework.cloud.kubernetes.commons.config.NamedConfigMapNormalizedSource;
21+
import org.springframework.cloud.kubernetes.commons.config.NormalizedSource;
22+
import org.springframework.cloud.kubernetes.configserver.KubernetesConfigServerApplication;
23+
import org.springframework.cloud.kubernetes.configserver.KubernetesPropertySourceSupplier;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.core.env.MapPropertySource;
26+
import org.springframework.http.HttpMethod;
27+
import org.springframework.http.ResponseEntity;
28+
import org.springframework.test.context.ActiveProfiles;
29+
import org.springframework.web.client.RestTemplate;
30+
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.Map;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
import static org.mockito.ArgumentMatchers.*;
37+
import static org.mockito.Mockito.mock;
38+
import static org.mockito.Mockito.when;
39+
40+
public class CompositeKubernetesIntegrationTests {
41+
@BeforeAll
42+
public static void before() {
43+
KUBERNETES_PROPERTY_SOURCE_SUPPLIER.add((coreApi, applicationName, namespace, springEnv) -> {
44+
List<MapPropertySource> propertySources = new ArrayList<>();
45+
46+
NormalizedSource defaultSource = new NamedConfigMapNormalizedSource(applicationName, "default", false,
47+
true);
48+
KubernetesClientConfigContext defaultContext = new KubernetesClientConfigContext(coreApi, defaultSource,
49+
"default", springEnv);
50+
propertySources.add(new KubernetesClientConfigMapPropertySource(defaultContext));
51+
return propertySources;
52+
});
53+
}
54+
55+
56+
@Nested
57+
@SpringBootTest(
58+
classes = {KubernetesConfigServerApplication.class},
59+
properties = {
60+
"spring.main.cloud-platform=KUBERNETES",
61+
"spring.cloud.kubernetes.client.namespace=default",
62+
"spring.config.name=compositeconfigserver",
63+
"spring.cloud.config.server.composite[0].type=kubernetes",
64+
"spring.cloud.kubernetes.secrets.enableApi=true"
65+
},
66+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
67+
)
68+
@ActiveProfiles({"test", "composite", "kubernetes"})
69+
class KubernetesConfigServerTest {
70+
71+
@LocalServerPort
72+
private int port;
73+
74+
@MockBean
75+
private CoreV1Api coreV1Api;
76+
77+
@TestConfiguration
78+
static class TestConfig {
79+
@Bean
80+
public CoreV1Api coreV1Api() {
81+
return mock(CoreV1Api.class);
82+
}
83+
}
84+
85+
@Test
86+
public void contextLoads() throws ApiException {
87+
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
88+
eq(null), eq(null), eq(null), eq(null), eq(null)))
89+
.thenReturn(CONFIGMAP_DEFAULT_LIST);
90+
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
91+
eq(null), eq(null), eq(null), eq(null), eq(null)))
92+
.thenReturn(SECRET_DEFAULT_LIST);
93+
94+
ResponseEntity<Environment> response = new RestTemplate().exchange(
95+
"http://localhost:" + this.port + "/gateway/default",
96+
HttpMethod.GET,
97+
null,
98+
Environment.class
99+
);
100+
101+
Environment environment = response.getBody();
102+
assertThat(environment).isNotNull();
103+
assertThat(environment.getPropertySources()).hasSize(4);
104+
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("configmap.gateway.default.default");
105+
assertThat(environment.getPropertySources().get(1).getName()).contains("secret.gateway.default.default");
106+
}
107+
}
108+
109+
@Nested
110+
@SpringBootTest(
111+
classes = {KubernetesConfigServerApplication.class, KubernetesNativeConfigServerTest.TestConfig.class},
112+
properties = {
113+
"spring.main.cloud-platform=KUBERNETES",
114+
"spring.cloud.kubernetes.client.namespace=default",
115+
"spring.cloud.config.server.composite[0].type=kubernetes",
116+
"spring.cloud.config.server.composite[1].type=native",
117+
"spring.cloud.config.server.composite[1].location=file:./native-config",
118+
"spring.cloud.kubernetes.secrets.enableApi=true"
119+
},
120+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
121+
)
122+
@ActiveProfiles({"test", "composite", "kubernetes", "native"})
123+
class KubernetesNativeConfigServerTest {
124+
125+
@LocalServerPort
126+
private int port;
127+
128+
@MockBean
129+
private CoreV1Api coreV1Api;
130+
131+
@SpyBean
132+
private NativeEnvironmentRepository nativeEnvironmentRepository;
133+
134+
@TestConfiguration
135+
static class TestConfig {
136+
@Bean
137+
public CoreV1Api coreV1Api() {
138+
return mock(CoreV1Api.class);
139+
}
140+
}
141+
142+
@Test
143+
public void testKubernetesAndNativeConfig() throws Exception {
144+
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
145+
eq(null), eq(null), eq(null), eq(null), eq(null)))
146+
.thenReturn(CONFIGMAP_DEFAULT_LIST);
147+
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
148+
eq(null), eq(null), eq(null), eq(null), eq(null)))
149+
.thenReturn(SECRET_DEFAULT_LIST);
150+
151+
Environment mockNativeEnvironment = new Environment("gateway", "default");
152+
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
153+
154+
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
155+
.thenReturn(mockNativeEnvironment);
156+
157+
158+
ResponseEntity<Environment> response = new RestTemplate().exchange(
159+
"http://localhost:" + this.port + "/gateway/default",
160+
HttpMethod.GET,
161+
null,
162+
Environment.class
163+
);
164+
165+
Environment environment = response.getBody();
166+
assertThat(environment).isNotNull();
167+
assertThat(environment.getPropertySources()).hasSizeGreaterThanOrEqualTo(5);
168+
169+
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("configmap.gateway.default.default");
170+
assertThat(environment.getPropertySources().get(1).getName()).contains("secret.gateway.default.default");
171+
172+
assertThat(environment.getPropertySources()).anyMatch(ps -> ps.getName().contains("native"));
173+
}
174+
}
175+
176+
177+
@Nested
178+
@SpringBootTest(
179+
classes = {KubernetesConfigServerApplication.class, KubernetesNativeConfigServerTest.TestConfig.class},
180+
properties = {
181+
"spring.main.cloud-platform=KUBERNETES",
182+
"spring.cloud.kubernetes.client.namespace=default",
183+
"spring.cloud.config.server.composite[0].type=kubernetes",
184+
"spring.cloud.config.server.composite[1].type=native",
185+
"spring.cloud.config.server.composite[1].location=file:./native-config",
186+
"spring.cloud.kubernetes.config.enableApi=false",
187+
"spring.cloud.kubernetes.secrets.enableApi=true"
188+
},
189+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
190+
)
191+
@ActiveProfiles({"test", "composite", "kubernetes", "native"})
192+
class KubernetesConfigMapDisabledNativeConfigServerTest {
193+
194+
@LocalServerPort
195+
private int port;
196+
197+
@MockBean
198+
private CoreV1Api coreV1Api;
199+
200+
@SpyBean
201+
private NativeEnvironmentRepository nativeEnvironmentRepository;
202+
203+
@TestConfiguration
204+
static class TestConfig {
205+
@Bean
206+
public CoreV1Api coreV1Api() {
207+
return mock(CoreV1Api.class);
208+
}
209+
}
210+
211+
@Test
212+
public void testKubernetesAndNativeConfig() throws Exception {
213+
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
214+
eq(null), eq(null), eq(null), eq(null), eq(null)))
215+
.thenReturn(CONFIGMAP_DEFAULT_LIST);
216+
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
217+
eq(null), eq(null), eq(null), eq(null), eq(null)))
218+
.thenReturn(SECRET_DEFAULT_LIST);
219+
220+
Environment mockNativeEnvironment = new Environment("gateway", "default");
221+
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
222+
223+
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
224+
.thenReturn(mockNativeEnvironment);
225+
226+
227+
ResponseEntity<Environment> response = new RestTemplate().exchange(
228+
"http://localhost:" + this.port + "/gateway/default",
229+
HttpMethod.GET,
230+
null,
231+
Environment.class
232+
);
233+
234+
Environment environment = response.getBody();
235+
assertThat(environment).isNotNull();
236+
assertThat(environment.getPropertySources()).hasSizeGreaterThanOrEqualTo(3);
237+
238+
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("secret.gateway.default.default");
239+
assertThat(environment.getPropertySources().get(1).getName()).contains("nativeProperties");
240+
241+
assertThat(environment.getPropertySources()).anyMatch(ps -> ps.getName().contains("native"));
242+
}
243+
}
244+
245+
246+
@Nested
247+
@SpringBootTest(
248+
classes = {KubernetesConfigServerApplication.class, KubernetesNativeConfigServerTest.TestConfig.class},
249+
properties = {
250+
"spring.main.cloud-platform=KUBERNETES",
251+
"spring.cloud.kubernetes.client.namespace=default",
252+
"spring.cloud.config.server.composite[0].type=kubernetes",
253+
"spring.cloud.config.server.composite[1].type=native",
254+
"spring.cloud.config.server.composite[1].location=file:./native-config"
255+
},
256+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
257+
)
258+
@ActiveProfiles({"test", "composite", "kubernetes", "native"})
259+
class KubernetesSecretsDisabledNativeConfigServerTest {
260+
261+
@LocalServerPort
262+
private int port;
263+
264+
@MockBean
265+
private CoreV1Api coreV1Api;
266+
267+
@SpyBean
268+
private NativeEnvironmentRepository nativeEnvironmentRepository;
269+
270+
@TestConfiguration
271+
static class TestConfig {
272+
@Bean
273+
public CoreV1Api coreV1Api() {
274+
return mock(CoreV1Api.class);
275+
}
276+
}
277+
278+
@Test
279+
public void testKubernetesAndNativeConfig() throws Exception {
280+
when(coreV1Api.listNamespacedConfigMap(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
281+
eq(null), eq(null), eq(null), eq(null), eq(null)))
282+
.thenReturn(CONFIGMAP_DEFAULT_LIST);
283+
when(coreV1Api.listNamespacedSecret(eq("default"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null),
284+
eq(null), eq(null), eq(null), eq(null), eq(null)))
285+
.thenReturn(SECRET_DEFAULT_LIST);
286+
287+
Environment mockNativeEnvironment = new Environment("gateway", "default");
288+
mockNativeEnvironment.add(new PropertySource("nativeProperties", Map.of("key1", "value1")));
289+
290+
when(nativeEnvironmentRepository.findOne(anyString(), anyString(), eq(null), anyBoolean()))
291+
.thenReturn(mockNativeEnvironment);
292+
293+
294+
ResponseEntity<Environment> response = new RestTemplate().exchange(
295+
"http://localhost:" + this.port + "/gateway/default",
296+
HttpMethod.GET,
297+
null,
298+
Environment.class
299+
);
300+
301+
Environment environment = response.getBody();
302+
assertThat(environment).isNotNull();
303+
assertThat(environment.getPropertySources()).hasSizeGreaterThanOrEqualTo(3);
304+
305+
assertThat(environment.getPropertySources().get(0).getName()).isEqualTo("configmap.gateway.default.default");
306+
assertThat(environment.getPropertySources().get(1).getName()).contains("nativeProperties");
307+
308+
assertThat(environment.getPropertySources()).anyMatch(ps -> ps.getName().contains("native"));
309+
}
310+
}
311+
312+
313+
private static final List<KubernetesPropertySourceSupplier> KUBERNETES_PROPERTY_SOURCE_SUPPLIER = new ArrayList<>();
314+
315+
private static V1ConfigMap buildConfigMap(String name, String namespace) {
316+
return new V1ConfigMapBuilder()
317+
.withMetadata(
318+
new V1ObjectMetaBuilder().withName(name).withNamespace(namespace).withResourceVersion("1").build())
319+
.addToData(Constants.APPLICATION_YAML, "dummy:\n property:\n string: \"" + name + "\"\n")
320+
.build();
321+
}
322+
323+
private static V1Secret buildSecret(String name, String namespace) {
324+
return new V1SecretBuilder()
325+
.withMetadata(
326+
new V1ObjectMetaBuilder().withName(name).withResourceVersion("0").withNamespace(namespace).build())
327+
.addToData("password", "p455w0rd".getBytes())
328+
.addToData("username", "user".getBytes())
329+
.build();
330+
}
331+
332+
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
333+
.addItemsItem(buildConfigMap("gateway", "default"));
334+
335+
private static final V1SecretList SECRET_DEFAULT_LIST = new V1SecretListBuilder()
336+
.addToItems(buildSecret("gateway", "default"))
337+
.build();
338+
339+
340+
}

0 commit comments

Comments
 (0)