Add Kubernetes Config Server (#881)

* Initial commit

* Added profile activation and support for secrets

* Cherry pick c48fdfd121

* Refactoring classnames that make more sense

* Refining configserver for milestone

* Remove unused code
This commit is contained in:
Ryan Baxter
2021-10-13 14:47:46 -04:00
committed by GitHub
parent c48fdfd121
commit 8951627d18
20 changed files with 1260 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
[#spring-cloud-kubernetes-configserver]
## Spring Cloud Kubernetes Config Server
The Spring Cloud Kubernetes Config Server, is based on https://spring.io/projects/spring-cloud-config[Spring Cloud Config Server] and adds an https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#_environment_repository[environment repository] for Kubernetes
https://kubernetes.io/docs/concepts/configuration/configmap/[Config Maps] and https://kubernetes.io/docs/concepts/configuration/secret/[Secrets].
This is component is completely optional. However, it allows you to continue to leverage configuration
you may have stored in existing environment repositories (Git, SVN, Vault, etc) with applications that you are running on Kubernetes.
A default image is located on Docker Hub which will allow you to easily get a Config Server deployed on Kubernetes without building
the code and image yourself. However, if you need to customize the config server behavior you can easily build your own
image from the source code on GitHub and use that.
### Configuration
#### Enabling The Kubernetes Environment Repository
To enable the Kubernetes environment repository the `kubernetes` profile must be included in the list of active profiles.
You may activate other profiles as well to use other environment repository implementations.
#### Config Map and Secret PropertySources
By default, only Config Map data will be fetched. To enable Secrets as well you will need to set `spring.cloud.kubernetes.secrets.enableApi=true`.
You can disable the Config Map `PropertySource` by setting `spring.cloud.kubernetes.config.enableApi=false`.
#### Fetching Config Map and Secret Data From Additional Namespaces
By default, the Kubernetes environment repository will only fetch Config Map and Secrets from the namespace in which it is deployed.
If you want to include data from other namespaces you can set `spring.cloud.kubernetes.configserver.config-map-namespaces` and/or `spring.cloud.kubernetes.configserver.secrets-namespaces` to a comma separated
list of namespace values.
NOTE: If you set `spring.cloud.kubernetes.configserver.config-map-namespaces` and/or `spring.cloud.kubernetes.configserver.secrets-namespaces`
you will need to include the namespace in which the Config Server is deployed in order to continue to fetch Config Map and Secret data from that namespace.
#### Kubernetes Access Controls
The Kubernetes Config Server uses the Kubernetes API server to fetch Config Map and Secret data. In order for it to do that
it needs ability to `get` and `list` Config Map and Secrets (depending on what you enable/disable).
### Deployment Yaml
Below is a sample deployment, service and permissions configuration you can use to deploy a basic Config Server to Kubernetes.
====
[source,yaml]
----
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver
spec:
ports:
- name: http
port: 8888
targetPort: 8888
selector:
app: spring-cloud-kubernetes-configserver
type: ClusterIP
- apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver:view
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-configserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
- apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-configserver-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-configserver
template:
metadata:
labels:
app: spring-cloud-kubernetes-configserver
spec:
serviceAccount: spring-cloud-kubernetes-configserver
containers:
- name: spring-cloud-kubernetes-configserver
image: springcloud/spring-cloud-kubernetes-configserver
imagePullPolicy: IfNotPresent
env:
- name: SPRING_PROFILES_INCLUDE
value: "kubernetes"
readinessProbe:
httpGet:
port: 8888
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8888
path: /actuator/health/liveness
ports:
- containerPort: 8888
----
====

View File

@@ -31,6 +31,8 @@ include::service-registry.adoc[]
include::spring-cloud-kubernetes-configuration-watcher.adoc[]
include::spring-cloud-kubernetes-configserver.adoc[]
include::examples.adoc[]
include::other-resources.adoc[]

View File

@@ -3,3 +3,4 @@ set -e
./mvnw deploy -DskipTests -B -Pfast,deploy ${@}
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-configuration-watcher -Pdockerpush ${@}
./mvnw dockerfile:push -pl :spring-cloud-kubernetes-configserver -Pdockerpush ${@}

View File

@@ -14,6 +14,7 @@
<modules>
<module>spring-cloud-kubernetes-configuration-watcher</module>
<module>spring-cloud-kubernetes-configserver</module>
</modules>
</project>

View File

@@ -0,0 +1,79 @@
---
apiVersion: v1
kind: List
items:
- apiVersion: v1
kind: Service
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver
spec:
ports:
- name: http
port: 8888
targetPort: 8888
selector:
app: spring-cloud-kubernetes-configserver
type: LoadBalancer
- apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app: spring-cloud-kubernetes-configserver
name: spring-cloud-kubernetes-configserver:view
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: namespace-reader
subjects:
- kind: ServiceAccount
name: spring-cloud-kubernetes-configserver
- apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: namespace-reader
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
- apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-kubernetes-configserver-deployment
spec:
selector:
matchLabels:
app: spring-cloud-kubernetes-configserver
template:
metadata:
labels:
app: spring-cloud-kubernetes-configserver
spec:
serviceAccount: spring-cloud-kubernetes-configserver
containers:
- name: spring-cloud-kubernetes-configserver
image: springcloud/spring-cloud-kubernetes-configserver:2.0.4-SNAPSHOT
imagePullPolicy: IfNotPresent
env:
- name: SPRING_PROFILES_INCLUDE
value: "kubernetes"
- name: SPRING_CLOUD_KUBERNETES_SECRETS_ENABLEAPI
value: "true"
readinessProbe:
httpGet:
port: 8888
path: /actuator/health/readiness
livenessProbe:
httpGet:
port: 8888
path: /actuator/health/liveness
ports:
- containerPort: 8888

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-kubernetes-controllers</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-kubernetes-configserver</artifactId>
<properties>
<jib.version>1.8.0</jib.version>
<base.image>openjdk:8u222-slim</base.image>
<docker.registry.organization>springcloud</docker.registry.organization>
<plexus-archiver.version>4.1.0</plexus-archiver.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-client-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${env.IMAGE}</name>
</image>
<goal>build-image</goal>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>build-image</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>dockerpush</id>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.12</version>
<configuration>
<repository>${docker.registry.organization}/${artifactId}</repository>
<tag>${project.version}</tag>
<username>${env.DOCKER_HUB_USERNAME}</username>
<password>${env.DOCKER_HUB_PASSWORD}</password>
<build>
<noCache>true</noCache>
</build>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-archiver</artifactId>
<version>${plexus-archiver.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>imagename</id>
<activation>
<property>
<name>!env.IMAGE</name>
</property>
</activation>
<properties>
<env.IMAGE>springcloud/${project.artifactId}:${project.version}</env.IMAGE>
</properties>
</profile>
<profile>
<id>jib</id>
<build>
<plugins>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>${jib.version}</version>
<configuration>
<from>
<image>${base.image}</image>
</from>
<to>
<image>spring-cloud/${project.artifactId}</image>
</to>
<container>
<user>nobody:nogroup</user>
<environment>
</environment>
</container>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>dockerBuild</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@@ -0,0 +1,20 @@
apiVersion: skaffold/v2alpha3
kind: Config
metadata:
name: spring-cloud-kubernetes-configserver
build:
artifacts:
- image: springcloud/spring-cloud-kubernetes-configserver
custom:
buildCommand: "../../mvnw clean install"
dependencies:
paths:
- src
- pom.xml
# jib: {
# args: ["-Pjib"]
# }
deploy:
kubectl:
manifests:
- k8s/deployment.yaml

View File

@@ -0,0 +1,35 @@
/*
* 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.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.config.server.ConfigServerApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
/**
* @author Ryan Baxter
*/
@SpringBootApplication
@EnableConfigServer
public class KubernetesConfigServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ConfigServerApplication.class).run(args);
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
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.ConditionalOnProperty;
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.KubernetesClientConfigMapPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesConfigEnabled;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesSecretsEnabled;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.MapPropertySource;
import static org.springframework.cloud.kubernetes.configserver.KubernetesPropertySourceSupplier.namespaceSplitter;
/**
* @author Ryan Baxter
*/
@Configuration
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class })
@AutoConfigureBefore({ ConfigServerAutoConfiguration.class })
@ConditionalOnKubernetesEnabled
@EnableConfigurationProperties(KubernetesConfigServerProperties.class)
public class KubernetesConfigServerAutoConfiguration {
@Bean
@Profile("kubernetes")
public EnvironmentRepository kubernetesEnvironmentRepository(CoreV1Api coreV1Api,
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers,
KubernetesNamespaceProvider kubernetesNamespaceProvider) {
return new KubernetesEnvironmentRepository(coreV1Api, kubernetesPropertySourceSuppliers,
kubernetesNamespaceProvider.getNamespace());
}
@Bean
@ConditionalOnKubernetesConfigEnabled
@ConditionalOnProperty(value = "spring.cloud.kubernetes.config.enableApi", matchIfMissing = true)
public KubernetesPropertySourceSupplier configMapPropertySourceSupplier(
KubernetesConfigServerProperties properties) {
return (coreApi, applicationName, namespace, springEnv) -> {
List<String> namespaces = namespaceSplitter(properties.getSecretsNamespaces(), namespace);
List<MapPropertySource> propertySources = new ArrayList<>();
namespaces.forEach(space -> propertySources
.add(new KubernetesClientConfigMapPropertySource(coreApi, applicationName, space, springEnv, "")));
return propertySources;
};
}
@Bean
@ConditionalOnKubernetesSecretsEnabled
@ConditionalOnProperty("spring.cloud.kubernetes.secrets.enableApi")
public KubernetesPropertySourceSupplier secretsPropertySourceSupplier(KubernetesConfigServerProperties properties) {
return (coreApi, applicationName, namespace, springEnv) -> {
List<String> namespaces = namespaceSplitter(properties.getSecretsNamespaces(), namespace);
List<MapPropertySource> propertySources = new ArrayList<>();
namespaces.forEach(space -> propertySources.add(new KubernetesClientSecretsPropertySource(coreApi,
applicationName, space, springEnv, new HashMap<>())));
return propertySources;
};
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.boot.context.properties.ConfigurationProperties;
/**
* @author Ryan Baxter
*/
@ConfigurationProperties("spring.cloud.kubernetes.configserver")
public class KubernetesConfigServerProperties {
private String conigMapNamespaces = "";
private String secretsNamespaces = "";
public String getConigMapNamespaces() {
return conigMapNamespaces;
}
public void setConigMapNamespaces(String conigMapNamespaces) {
this.conigMapNamespaces = conigMapNamespaces;
}
public String getSecretsNamespaces() {
return secretsNamespaces;
}
public void setSecretsNamespaces(String secretsNamespaces) {
this.secretsNamespaces = secretsNamespaces;
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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 java.util.List;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.apache.commons.logging.Log;
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.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.StringUtils;
/**
* @author Ryan Baxter
*/
public class KubernetesEnvironmentRepository implements EnvironmentRepository {
private static final Log LOG = LogFactory.getLog(KubernetesEnvironmentRepository.class);
private CoreV1Api coreApi;
private List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers;
private String namespace;
public KubernetesEnvironmentRepository(CoreV1Api coreApi,
List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers, String namespace) {
this.coreApi = coreApi;
this.kubernetesPropertySourceSuppliers = kubernetesPropertySourceSuppliers;
this.namespace = namespace;
}
@Override
public Environment findOne(String application, String profile, String label) {
return findOne(application, profile, label, true);
}
@Override
public Environment findOne(String application, String profile, String label, boolean includeOrigin) {
String[] profiles = StringUtils.commaDelimitedListToStringArray(profile);
LOG.info("Profiles: " + profile);
LOG.info("Application: " + application);
LOG.info("Label: " + label);
Environment environment = new Environment(application, profiles, label, null, null);
try {
StandardEnvironment springEnv = new StandardEnvironment();
springEnv.setActiveProfiles(profiles);
addApplicationConfiguration(environment, springEnv, "application");
if (!"application".equalsIgnoreCase(application)) {
addApplicationConfiguration(environment, springEnv, application);
}
return environment;
}
catch (Exception e) {
LOG.warn(e);
}
return environment;
}
private void addApplicationConfiguration(Environment environment, StandardEnvironment springEnv,
String applicationName) {
kubernetesPropertySourceSuppliers.stream().forEach(supplier -> {
List<MapPropertySource> propertySources = supplier.get(coreApi, applicationName, namespace, springEnv);
propertySources.forEach(propertySource -> {
if (propertySource.getPropertyNames().length > 0) {
LOG.debug("Adding PropertySource " + propertySource.getName());
LOG.debug("PropertySource Names: "
+ StringUtils.arrayToCommaDelimitedString(propertySource.getPropertyNames()));
environment.add(new PropertySource(propertySource.getName(), propertySource.getSource()));
}
});
});
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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 java.util.Arrays;
import java.util.Collections;
import java.util.List;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.util.StringUtils;
/**
* @author Ryan Baxter
*/
public interface KubernetesPropertySourceSupplier {
List<MapPropertySource> get(CoreV1Api coreV1Api, String name, String namespace, Environment environment);
static List<String> namespaceSplitter(String namespacesString, String currentNamespace) {
List<String> namespaces = Collections.singletonList(currentNamespace);
String[] namespacesArray = StringUtils.commaDelimitedListToStringArray(namespacesString);
if (namespacesArray.length > 0) {
namespaces = Arrays.asList(namespacesArray);
}
return namespaces;
}
}

View File

@@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.configserver.KubernetesConfigServerAutoConfiguration

View File

@@ -0,0 +1,6 @@
spring:
application:
name: kubernetesconfigserver
server:
port: 8888

View File

@@ -0,0 +1,88 @@
/*
* 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 com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ListMetaBuilder;
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.BeforeEach;
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.web.client.TestRestTemplate;
import org.springframework.cloud.config.environment.Environment;
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.urlMatching;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "spring.cloud.kubernetes.client.namespace=default", "spring.profiles.include=kubernetes",
"spring.cloud.kubernetes.secrets.enableApi=true", "debug=true" },
classes = { KubernetesConfigServerApplication.class })
public class ConfigServerIntegrationTest {
@Autowired
private TestRestTemplate testRestTemplate;
public static WireMockServer wireMockServer;
@BeforeEach
public void beforeEach() {
V1ConfigMapList TEST_CONFIGMAP = new V1ConfigMapList().addItemsItem(new V1ConfigMapBuilder().withMetadata(
new V1ObjectMetaBuilder().withName("test-cm").withNamespace("default").withResourceVersion("1").build())
.addToData("app.name", "test").build());
V1SecretList TEST_SECRET = new V1SecretListBuilder()
.withMetadata(new V1ListMetaBuilder().withResourceVersion("1").build())
.addToItems(new V1SecretBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("test-cm").withResourceVersion("0")
.withNamespace("default").build())
.addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build())
.build();
WireMock.stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*"))
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(TEST_CONFIGMAP))));
WireMock.stubFor(get(urlMatching("^/api/v1/namespaces/default/secrets.*"))
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(TEST_SECRET))));
}
@Test
public void enabled() throws Exception {
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();
assertThat(env.getPropertySources().get(0).getSource().get("app.name")).isEqualTo("test");
assertThat(env.getPropertySources().get(1).getName().equals("secrets.test-cm.default")).isTrue();
assertThat(env.getPropertySources().get(1).getSource().get("password")).isEqualTo("p455w0rd");
assertThat(env.getPropertySources().get(1).getSource().get("username")).isEqualTo("user");
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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",
"spring.cloud.kubernetes.enabled=false", "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.cloud.kubernetes.enabled=false",
"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.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.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.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.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.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,247 @@
/*
* 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
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.V1SecretBuilder;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.openapi.models.V1SecretListBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
import org.springframework.core.env.MapPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Ryan Baxter
*/
class KubernetesEnvironmentRepositoryTests {
private static List<KubernetesPropertySourceSupplier> kubernetesPropertySourceSuppliers = new ArrayList<>();
private static final V1ConfigMapList CONFIGMAP_DEFAULT_LIST = new V1ConfigMapList()
.addItemsItem(new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("application").withNamespace("default")
.withResourceVersion("1").build())
.addToData("application.yaml",
"dummy:\n property:\n string2: \"a\"\n int2: 1\n bool2: true\n")
.build())
.addItemsItem(new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("stores").withNamespace("default")
.withResourceVersion("1").build())
.addToData("application.yaml",
"dummy:\n property:\n string2: \"a\"\n int2: 1\n bool2: true\n")
.build())
.addItemsItem(new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("stores-dev").withNamespace("default")
.withResourceVersion("1").build())
.addToData("application.yaml",
"dummy:\n property:\n string1: \"a\"\n string2: \"b\"\n int2: 2\n bool2: false\n")
.build());
private static final V1ConfigMapList CONFIGMAP_DEV_LIST = new V1ConfigMapList()
.addItemsItem(new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("stores").withNamespace("dev")
.withResourceVersion("1").build())
.addToData("application.yaml",
"dummy:\n property:\n string2: \"dev\"\n int2: 1\n bool2: true\n")
.build());
private static final V1SecretList SECRET_LIST = new V1SecretListBuilder()
.addToItems(new V1SecretBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("application").withResourceVersion("0")
.withNamespace("default").build())
.addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build())
.addToItems(new V1SecretBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("stores").withResourceVersion("0")
.withNamespace("default").build())
.addToData("password", "p455w0rd".getBytes()).addToData("username", "stores".getBytes()).build())
.addToItems(new V1SecretBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("stores-dev").withResourceVersion("0")
.withNamespace("default").build())
.addToData("password", "p455w0rd".getBytes()).addToData("username", "stores-dev".getBytes())
.build())
.build();
@BeforeAll
public static void before() {
kubernetesPropertySourceSuppliers.add((coreApi, applicationName, namespace, springEnv) -> {
List<MapPropertySource> propertySources = new ArrayList<>();
propertySources.add(
new KubernetesClientConfigMapPropertySource(coreApi, applicationName, "default", springEnv, ""));
propertySources
.add(new KubernetesClientConfigMapPropertySource(coreApi, applicationName, "dev", springEnv, ""));
return propertySources;
});
kubernetesPropertySourceSuppliers.add((coreApi, applicationName, namespace, springEnv) -> {
List<MapPropertySource> propertySources = new ArrayList<>();
propertySources.add(new KubernetesClientSecretsPropertySource(coreApi, applicationName, "default",
springEnv, new HashMap<>()));
return propertySources;
});
}
@Test
public void testApplicationCase() throws ApiException {
CoreV1Api coreApi = mock(CoreV1Api.class);
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))).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))).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))).thenReturn(CONFIGMAP_DEV_LIST);
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
kubernetesPropertySourceSuppliers, "default");
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("secrets.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");
}
});
}
@Test
public void testStoresCase() throws ApiException {
CoreV1Api coreApi = mock(CoreV1Api.class);
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))).thenReturn(CONFIGMAP_DEFAULT_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))).thenReturn(CONFIGMAP_DEV_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))).thenReturn(SECRET_LIST);
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
kubernetesPropertySourceSuppliers, "default");
Environment environment = environmentRepository.findOne("stores", "", "");
assertThat(environment.getPropertySources().size()).isEqualTo(5);
environment.getPropertySources().forEach(propertySource -> {
assertThat(propertySource.getName().equals("configmap.application.default")
|| propertySource.getName().equals("secrets.application.default")
|| propertySource.getName().equals("configmap.stores.default")
|| propertySource.getName().equals("configmap.stores.dev")
|| propertySource.getName().equals("secrets.stores.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");
}
if (propertySource.getName().equals("configmap.stores.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("configmap.stores.dev")) {
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("dev");
}
if (propertySource.getName().equals("secrets.stores.default")) {
assertThat(propertySource.getSource().size()).isEqualTo(2);
assertThat(propertySource.getSource().get("username")).isEqualTo("stores");
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
}
});
}
@Test
public void testStoresProfileCase() throws ApiException {
CoreV1Api coreApi = mock(CoreV1Api.class);
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))).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))).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))).thenReturn(CONFIGMAP_DEV_LIST);
KubernetesEnvironmentRepository environmentRepository = new KubernetesEnvironmentRepository(coreApi,
kubernetesPropertySourceSuppliers, "default");
Environment environment = environmentRepository.findOne("stores", "dev", "");
assertThat(environment.getPropertySources().size()).isEqualTo(5);
environment.getPropertySources().forEach(propertySource -> {
assertThat(propertySource.getName().equals("configmap.application.default")
|| propertySource.getName().equals("secrets.application.default")
|| propertySource.getName().equals("configmap.stores.default")
|| propertySource.getName().equals("configmap.stores.dev")
|| propertySource.getName().equals("secrets.stores.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");
}
if (propertySource.getName().equals("configmap.stores.default")) {
assertThat(propertySource.getSource().size()).isEqualTo(4);
assertThat(propertySource.getSource().get("dummy.property.int2")).isEqualTo(2);
assertThat(propertySource.getSource().get("dummy.property.bool2")).isEqualTo(false);
assertThat(propertySource.getSource().get("dummy.property.string2")).isEqualTo("b");
assertThat(propertySource.getSource().get("dummy.property.string1")).isEqualTo("a");
}
if (propertySource.getName().equals("configmap.stores.dev")) {
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("dev");
}
// Currently KubernetesClientSecretsPropertySource does not take into account
// profiles, so that plays no role at the moment
// See https://github.com/spring-cloud/spring-cloud-kubernetes/issues/880
if (propertySource.getName().equals("secrets.stores.default")) {
assertThat(propertySource.getSource().size()).isEqualTo(2);
assertThat(propertySource.getSource().get("username")).isEqualTo("stores");
assertThat(propertySource.getSource().get("password")).isEqualTo("p455w0rd");
}
});
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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 com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.util.ClientBuilder;
import org.springframework.context.annotation.Bean;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
/**
* @author Ryan Baxter
*/
public class TestBootstrapConfig {
@Bean
WireMockServer wireMockServer() {
WireMockServer wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
return wireMockServer;
}
@Bean
ApiClient apiClient(WireMockServer wireMockServer) {
ApiClient apiClient = new ClientBuilder().setBasePath(wireMockServer.baseUrl()).build();
apiClient.setDebugging(true);
return apiClient;
}
@Bean
CoreV1Api coreApi(ApiClient apiClient) {
return new CoreV1Api(apiClient);
}
}

View File

@@ -0,0 +1,4 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.configserver.TestBootstrapConfig
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.configserver.KubernetesConfigServerAutoConfiguration

View File

@@ -13,4 +13,5 @@
<suppress files=".*PollingConfigurationChangeDetector.*" checks="LineLength*"/>
<suppress files=".*LeaderRecordWatcherTest.*" checks="LineLength*"/>
<suppress files=".*ConfigurationWatcherApplication\.java" checks="HideUtilityClassConstructor"/>
<suppress files=".*KubernetesConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
</suppressions>