Refactor Config Module And Add A New Config Module Using Kubernetes Client (#664)

* Deprecate KubernetesAutoServiceRegistration

* Document service registry in kubernetes.  Fixes #348

* Refactoring config module to extract common code
Renamed config module to be specific to fabric8

* Kubernetes client config module

* Fixing test to avoid 503 error

* Accounting for missing invocation

* Adding code to deal with potential 503 error

* Fixing class used in polling change listener

* Formatting changes
This commit is contained in:
Ryan Baxter
2020-11-24 20:25:20 -05:00
committed by GitHub
parent d2704f0174
commit a47a530bc3
137 changed files with 3691 additions and 617 deletions

View File

@@ -14,7 +14,7 @@ The link:./spring-cloud-kubernetes-config[Spring Cloud Kubernetes Config] projec
during application bootstrapping and triggers hot reloading of beans or Spring context when changes are detected on
observed `ConfigMap` instances.
The default behavior is to create a `ConfigMapPropertySource` based on a Kubernetes `ConfigMap` that has a `metadata.name` value of either the name of
The default behavior is to create a `Fabric8ConfigMapPropertySource` based on a Kubernetes `ConfigMap` that has a `metadata.name` value of either the name of
your Spring application (as defined by its `spring.application.name` property) or a custom name defined within the
`bootstrap.properties` file under the following key: `spring.cloud.kubernetes.config.name`.
@@ -300,7 +300,7 @@ Kubernetes has the notion of https://kubernetes.io/docs/concepts/configuration/s
sensitive data such as passwords, OAuth tokens, and so on. This project provides integration with `Secrets` to make secrets
accessible by Spring Boot applications. You can explicitly enable or disable This feature by setting the `spring.cloud.kubernetes.secrets.enabled` property.
When enabled, the `SecretsPropertySource` looks up Kubernetes for `Secrets` from the following sources:
When enabled, the `Fabric8SecretsPropertySource` looks up Kubernetes for `Secrets` from the following sources:
. Reading recursively from secrets mounts
. Named after the application (as defined by `spring.application.name`)

View File

@@ -90,8 +90,9 @@
<module>spring-cloud-kubernetes-commons</module>
<module>spring-cloud-kubernetes-test-support</module>
<module>spring-cloud-kubernetes-client-autoconfig</module>
<module>spring-cloud-kubernetes-client-config</module>
<module>spring-cloud-kubernetes-fabric8-autoconfig</module>
<module>spring-cloud-kubernetes-config</module>
<module>spring-cloud-kubernetes-fabric8-config</module>
<module>spring-cloud-kubernetes-discovery</module>
<module>spring-cloud-starter-kubernetes</module>
<module>spring-cloud-starter-kubernetes-config</module>

View File

@@ -20,9 +20,6 @@ import java.io.IOException;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.util.ClientBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor;
@@ -37,6 +34,8 @@ import org.springframework.cloud.kubernetes.commons.PodUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient;
/**
* @author Ryan Baxter
*/
@@ -45,36 +44,11 @@ import org.springframework.context.annotation.Configuration;
@AutoConfigureAfter(KubernetesCommonsAutoConfiguration.class)
public class KubernetesClientAutoConfiguration {
private static final Log LOG = LogFactory.getLog(KubernetesClientAutoConfiguration.class);
private ApiClient kubernetesApiClient() throws IOException {
try {
// Assume we are running in a cluster
ApiClient apiClient = ClientBuilder.cluster().build();
io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient);
LOG.info("Created API client in the cluster.");
return apiClient;
}
catch (Exception e) {
LOG.info(
"Could not create the Kubernetes ApiClient in a cluster environment, trying to use a \"standard\" configuration instead.",
e);
try {
ApiClient apiClient = ClientBuilder.standard().build();
io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient);
return apiClient;
}
catch (IOException e1) {
LOG.warn("Could not create a Kubernetes ApiClient from either a cluster or standard environment", e1);
throw e1;
}
}
}
@Bean
@ConditionalOnMissingBean
public CoreV1Api coreApi() throws IOException {
kubernetesApiClient();
ApiClient apiClient = kubernetesApiClient();
io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient);
return new CoreV1Api();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client;
import java.io.IOException;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.util.ClientBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Ryan Baxter
*/
public final class KubernetesClientUtils {
private static final Log LOG = LogFactory.getLog(KubernetesClientUtils.class);
private KubernetesClientUtils() {
}
public static ApiClient kubernetesApiClient() throws IOException {
try {
// Assume we are running in a cluster
ApiClient apiClient = ClientBuilder.cluster().build();
LOG.info("Created API client in the cluster.");
return apiClient;
}
catch (Exception e) {
LOG.info(
"Could not create the Kubernetes ApiClient in a cluster environment, trying to use a \"standard\" configuration instead.",
e);
try {
ApiClient apiClient = ClientBuilder.standard().build();
return apiClient;
}
catch (IOException e1) {
LOG.warn("Could not create a Kubernetes ApiClient from either a cluster or standard environment", e1);
throw e1;
}
}
}
}

View File

@@ -22,19 +22,19 @@ import org.springframework.cloud.kubernetes.client.KubernetesClientPodUtils;
import org.springframework.cloud.kubernetes.commons.profile.AbstractKubernetesProfileEnvironmentPostProcessor;
import org.springframework.core.env.Environment;
import static io.kubernetes.client.util.Config.ENV_SERVICE_HOST;
/**
* @author Ryan Baxter
* @author Thomas Vitale
*/
public class KubernetesClientProfileEnvironmentPostProcessor extends AbstractKubernetesProfileEnvironmentPostProcessor {
protected static final String KUBERNETES_SERVICE_ENV_VAR = "KUBERNETES_SERVICE_HOST";
@Override
protected boolean isInsideKubernetes(Environment environment) {
CoreV1Api api = new CoreV1Api();
KubernetesClientPodUtils utils = new KubernetesClientPodUtils(api, environment.getProperty(NAMESPACE_PROPERTY));
return environment.containsProperty(KUBERNETES_SERVICE_ENV_VAR) || utils.isInsideKubernetes();
return environment.containsProperty(ENV_SERVICE_HOST) || utils.isInsideKubernetes();
}
}

View File

@@ -0,0 +1,91 @@
<?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</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-kubernetes-client-config</artifactId>
<properties>
<wiremock.version>2.26.3</wiremock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-autoconfig</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-commons</artifactId>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java</artifactId>
</dependency>
<dependency>
<groupId>io.kubernetes</groupId>
<artifactId>client-java-extended</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-rsa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>${wiremock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author Ryan Baxter
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnKubernetesEnabled
@AutoConfigureAfter(KubernetesBootstrapConfiguration.class)
public class KubernetesClientBootstrapConfiguration {
@Configuration(proxyBeanMethods = false)
@Import({ KubernetesCommonsAutoConfiguration.class, KubernetesClientAutoConfiguration.class })
protected static class KubernetesPropertySourceConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.config.enabled", matchIfMissing = true)
public KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator(
ConfigMapConfigProperties properties, CoreV1Api coreV1Api,
KubernetesClientProperties kubernetesClientProperties) {
return new KubernetesClientConfigMapPropertySourceLocator(coreV1Api, properties,
kubernetesClientProperties);
}
@Bean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.secrets.enabled", matchIfMissing = true)
public KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator(
SecretsConfigProperties properties, CoreV1Api coreV1Api,
KubernetesClientProperties kubernetesClientProperties) {
return new KubernetesClientSecretsPropertySourceLocator(coreV1Api, kubernetesClientProperties, properties);
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySource;
import org.springframework.core.env.Environment;
/**
* @author Ryan Baxter
*/
public class KubernetesClientConfigMapPropertySource extends ConfigMapPropertySource {
private static final Log LOG = LogFactory.getLog(KubernetesClientConfigMapPropertySource.class);
public KubernetesClientConfigMapPropertySource(CoreV1Api coreV1Api, String name, String namespace,
Environment environment) {
super(getName(name, namespace), asObjectMap(getData(coreV1Api, name, namespace, environment)));
}
private static Map<String, Object> getData(CoreV1Api coreV1Api, String name, String namespace,
Environment environment) {
try {
List<String> names = new ArrayList<>();
names.add(name);
if (environment != null) {
for (String activeProfile : environment.getActiveProfiles()) {
names.add(name + "-" + activeProfile);
}
}
Map<String, Object> result = new LinkedHashMap<>();
coreV1Api.listNamespacedConfigMap(namespace, null, null, null, null, null, null, null, null, null)
.getItems().stream().filter(cm -> names.contains(cm.getMetadata().getName()))
.forEach(map -> result.putAll(processAllEntries(map.getData(), environment)));
return result;
}
catch (ApiException e) {
LOG.warn("Unable to get ConfigMap " + name + " in namespace " + namespace, e);
}
return new LinkedHashMap<>();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigUtils.getNamespace;
/**
* @author Ryan Baxter
*/
public class KubernetesClientConfigMapPropertySourceLocator extends ConfigMapPropertySourceLocator {
private CoreV1Api coreV1Api;
private KubernetesClientProperties kubernetesClientProperties;
public KubernetesClientConfigMapPropertySourceLocator(CoreV1Api coreV1Api, ConfigMapConfigProperties properties,
KubernetesClientProperties kubernetesClientProperties) {
super(properties);
this.coreV1Api = coreV1Api;
this.kubernetesClientProperties = kubernetesClientProperties;
}
@Override
protected MapPropertySource getMapPropertySource(String name,
ConfigMapConfigProperties.NormalizedSource normalizedSource, String configurationTarget,
ConfigurableEnvironment environment) {
return new KubernetesClientConfigMapPropertySource(coreV1Api, name,
getNamespace(normalizedSource, kubernetesClientProperties), environment);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.util.StringUtils;
/**
* @author Ryan Baxter
*/
public final class KubernetesClientConfigUtils {
private KubernetesClientConfigUtils() {
}
public static String getNamespace(ConfigMapConfigProperties.NormalizedSource normalizedSource,
KubernetesClientProperties kubernetesClientProperties) {
if (!StringUtils.hasText(normalizedSource.getNamespace())) {
return kubernetesClientProperties.getNamespace();
}
else {
return normalizedSource.getNamespace();
}
}
public static String getNamespace(SecretsConfigProperties.NormalizedSource normalizedSource,
KubernetesClientProperties kubernetesClientProperties) {
if (!StringUtils.hasText(normalizedSource.getNamespace())) {
return kubernetesClientProperties.getNamespace();
}
else {
return normalizedSource.getNamespace();
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Secret;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* @author Ryan Baxter
*/
public class KubernetesClientSecretsPropertySource extends SecretsPropertySource {
private static final Log LOG = LogFactory.getLog(KubernetesClientSecretsPropertySource.class);
private CoreV1Api coreV1Api;
public KubernetesClientSecretsPropertySource(CoreV1Api coreV1Api, String name, String namespace,
Environment environment, Map<String, String> labels) {
super(getSourceName(name, namespace), getSourceData(coreV1Api, environment, name, namespace, labels));
}
private static Map<String, Object> getSourceData(CoreV1Api api, Environment env, String name, String namespace,
Map<String, String> labels) {
Map<String, Object> result = new HashMap<>();
try {
// Read for secrets api (named)
if (StringUtils.hasText(name)) {
Optional<V1Secret> secret;
if (!StringUtils.hasText(namespace)) {
// There could technically be more than one, just return the first
secret = api.listSecretForAllNamespaces(null, null, null, null, null, null, null, null, null)
.getItems().stream().filter(s -> name.equals(s.getMetadata().getName())).findFirst();
}
else {
secret = api.listNamespacedSecret(namespace, null, null, null, null, null, null, null, null, null)
.getItems().stream().filter(s -> name.equals(s.getMetadata().getName())).findFirst();
}
secret.ifPresent(s -> putAll(s, result));
}
// Read for secrets api (label)
if (labels != null && !labels.isEmpty()) {
if (!StringUtils.hasText(namespace)) {
api.listSecretForAllNamespaces(null, null, null, createLabelsSelector(labels), null, null, null,
null, null).getItems().forEach(s -> putAll(s, result));
}
else {
api.listNamespacedSecret(namespace, null, null, null, null, createLabelsSelector(labels), null,
null, null, null).getItems().forEach(s -> putAll(s, result));
}
}
}
catch (Exception e) {
LOG.warn("Can't read secret with name: [" + name + "] or labels [" + labels + "] in namespace:[" + namespace
+ "] (cause: " + e.getMessage() + "). Ignoring", e);
}
return result;
}
private static String createLabelsSelector(Map<String, String> labels) {
StringBuilder selectorString = new StringBuilder();
for (String key : labels.keySet()) {
if (selectorString.length() != 0) {
selectorString.append(",");
}
selectorString.append(key + "=" + labels.get(key));
}
return selectorString.toString();
}
private static void putAll(V1Secret secret, Map<String, Object> result) {
Map<String, String> secretData = new HashMap<>();
secret.getData().forEach((key, value) -> secretData.put(key, Base64.getEncoder().encodeToString(value)));
if (secret != null) {
putAll(secretData, result);
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import static org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigUtils.getNamespace;
import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName;
/**
* @author Ryan Baxter
*/
public class KubernetesClientSecretsPropertySourceLocator extends SecretsPropertySourceLocator {
private CoreV1Api coreV1Api;
private KubernetesClientProperties kubernetesClientProperties;
public KubernetesClientSecretsPropertySourceLocator(CoreV1Api coreV1Api,
KubernetesClientProperties kubernetesClientProperties, SecretsConfigProperties secretsConfigProperties) {
super(secretsConfigProperties);
this.coreV1Api = coreV1Api;
this.kubernetesClientProperties = kubernetesClientProperties;
}
@Override
protected MapPropertySource getPropertySource(ConfigurableEnvironment environment,
SecretsConfigProperties.NormalizedSource normalizedSource, String configurationTarget) {
return new KubernetesClientSecretsPropertySource(coreV1Api,
getApplicationName(environment, normalizedSource.getName(), configurationTarget),
getNamespace(normalizedSource, kubernetesClientProperties), environment, normalizedSource.getLabels());
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config.reload;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @author Ryan Baxter
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnKubernetesEnabled
@ConditionalOnClass(EndpointAutoConfiguration.class)
@AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class,
RefreshAutoConfiguration.class, ConfigReloadAutoConfiguration.class })
@EnableConfigurationProperties(ConfigReloadProperties.class)
public class KubernetesClientConfigReloadAutoConfiguration {
/**
* Configuration reload must be enabled explicitly.
*/
@ConditionalOnProperty("spring.cloud.kubernetes.reload.enabled")
@ConditionalOnClass({ RestartEndpoint.class, ContextRefresher.class })
@EnableScheduling
@EnableAsync
protected static class ConfigReloadAutoConfigurationBeans {
/**
* Polling configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator,
AbstractEnvironment environment) {
return new PollingConfigMapChangeDetector(environment, properties, strategy,
KubernetesClientConfigMapPropertySource.class, configMapPropertySourceLocator);
}
/**
* Polling secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator,
AbstractEnvironment environment) {
return new PollingSecretsChangeDetector(environment, properties, strategy,
KubernetesClientSecretsPropertySource.class, secretsPropertySourceLocator);
}
/**
* Event Based configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configMap change events and fire a reload.
*/
@Bean
@ConditionalOnBean(KubernetesClientConfigMapPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
KubernetesClientConfigMapPropertySourceLocator configMapPropertySourceLocator,
AbstractEnvironment environment, CoreV1Api coreV1Api,
KubernetesClientProperties kubernetesClientProperties) {
return new KubernetesClientEventBasedConfigMapChangeDetector(coreV1Api, environment, properties, strategy,
configMapPropertySourceLocator, kubernetesClientProperties);
}
/**
* Event Based secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @return a bean that listen to secrets change events and fire a reload.
*/
@Bean
@ConditionalOnBean(KubernetesClientSecretsPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
KubernetesClientSecretsPropertySourceLocator secretsPropertySourceLocator,
AbstractEnvironment environment, CoreV1Api coreV1Api,
KubernetesClientProperties kubernetesClientProperties) {
return new KubernetesClientEventBasedSecretsChangeDetector(coreV1Api, environment, properties, strategy,
secretsPropertySourceLocator, kubernetesClientProperties);
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config.reload;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import io.kubernetes.client.informer.ResourceEventHandler;
import io.kubernetes.client.informer.SharedIndexInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.util.CallGeneratorParams;
import okhttp3.OkHttpClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient;
/**
* @author Ryan Baxter
*/
public class KubernetesClientEventBasedConfigMapChangeDetector extends ConfigurationChangeDetector {
private static final Log LOG = LogFactory.getLog(KubernetesClientEventBasedConfigMapChangeDetector.class);
private CoreV1Api coreV1Api = null;
private KubernetesClientConfigMapPropertySourceLocator propertySourceLocator;
private SharedInformerFactory factory;
private KubernetesClientProperties kubernetesClientProperties;
public KubernetesClientEventBasedConfigMapChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
KubernetesClientConfigMapPropertySourceLocator propertySourceLocator,
KubernetesClientProperties kubernetesClientProperties) {
super(environment, properties, strategy);
this.propertySourceLocator = propertySourceLocator;
this.coreV1Api = coreV1Api;
this.factory = new SharedInformerFactory();
this.kubernetesClientProperties = kubernetesClientProperties;
}
public KubernetesClientEventBasedConfigMapChangeDetector(ConfigurableEnvironment environment,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
KubernetesClientConfigMapPropertySourceLocator propertySourceLocator,
KubernetesClientProperties kubernetesClientProperties) {
super(environment, properties, strategy);
this.propertySourceLocator = propertySourceLocator;
this.kubernetesClientProperties = kubernetesClientProperties;
try {
ApiClient apiClient = kubernetesApiClient();
OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
apiClient.setHttpClient(httpClient);
this.coreV1Api = new CoreV1Api(apiClient);
}
catch (IOException e) {
LOG.error("Failed to create Kubernetes API client. Event based ConfigMap monitoring will not work", e);
}
this.factory = new SharedInformerFactory();
}
@PostConstruct
public void watch() {
if (coreV1Api != null && this.properties.isMonitoringConfigMaps()) {
SharedIndexInformer<V1ConfigMap> configMapInformer = factory.sharedIndexInformerFor(
(CallGeneratorParams params) -> coreV1Api.listNamespacedConfigMapCall(
kubernetesClientProperties.getNamespace(), null, null, null, null, null, null,
params.resourceVersion, params.timeoutSeconds, params.watch, null),
V1ConfigMap.class, V1ConfigMapList.class);
configMapInformer.addEventHandler(new ResourceEventHandler<V1ConfigMap>() {
@Override
public void onAdd(V1ConfigMap obj) {
LOG.info("CongifMap " + obj.getMetadata().getName() + " was added.");
onEvent(obj);
}
@Override
public void onUpdate(V1ConfigMap oldObj, V1ConfigMap newObj) {
LOG.info("ConfigMap " + newObj.getMetadata().getName() + " was added.");
onEvent(newObj);
}
@Override
public void onDelete(V1ConfigMap obj, boolean deletedFinalStateUnknown) {
LOG.info("ConfigMap " + obj.getMetadata() + " was deleted.");
onEvent(obj);
}
});
factory.startAllRegisteredInformers();
}
}
@PreDestroy
public void unwatch() {
factory.stopAllRegisteredInformers();
}
private void onEvent(V1ConfigMap configMap) {
boolean changed = changed(locateMapPropertySources(this.propertySourceLocator, this.environment),
findPropertySources(KubernetesClientConfigMapPropertySource.class));
if (changed) {
LOG.info("Configuration change detected, reloading properties.");
reloadProperties();
}
else {
LOG.warn("Configuration change was not detected.");
}
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config.reload;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import io.kubernetes.client.informer.ResourceEventHandler;
import io.kubernetes.client.informer.SharedIndexInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.CallGeneratorParams;
import okhttp3.OkHttpClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.core.env.ConfigurableEnvironment;
import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient;
/**
* @author Ryan Baxter
*/
public class KubernetesClientEventBasedSecretsChangeDetector extends ConfigurationChangeDetector {
private static final Log LOG = LogFactory.getLog(KubernetesClientEventBasedSecretsChangeDetector.class);
private CoreV1Api coreV1Api;
private KubernetesClientSecretsPropertySourceLocator propertySourceLocator;
private SharedInformerFactory factory;
private KubernetesClientProperties kubernetesClientProperties;
public KubernetesClientEventBasedSecretsChangeDetector(CoreV1Api coreV1Api, ConfigurableEnvironment environment,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
KubernetesClientSecretsPropertySourceLocator propertySourceLocator,
KubernetesClientProperties kubernetesClientProperties) {
super(environment, properties, strategy);
this.propertySourceLocator = propertySourceLocator;
this.factory = new SharedInformerFactory();
this.coreV1Api = coreV1Api;
this.kubernetesClientProperties = kubernetesClientProperties;
}
public KubernetesClientEventBasedSecretsChangeDetector(ConfigurableEnvironment environment,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
KubernetesClientSecretsPropertySourceLocator propertySourceLocator,
KubernetesClientProperties kubernetesClientProperties) {
super(environment, properties, strategy);
this.propertySourceLocator = propertySourceLocator;
this.factory = new SharedInformerFactory();
this.kubernetesClientProperties = kubernetesClientProperties;
try {
ApiClient apiClient = kubernetesApiClient();
OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
apiClient.setHttpClient(httpClient);
this.coreV1Api = new CoreV1Api(apiClient);
}
catch (IOException e) {
LOG.error("Failed to create Kubernetes API client. Event based ConfigMap monitoring will not work", e);
}
}
@PostConstruct
public void watch() {
if (coreV1Api != null && this.properties.isMonitoringSecrets()) {
SharedIndexInformer<V1Secret> configMapInformer = factory.sharedIndexInformerFor(
(CallGeneratorParams params) -> coreV1Api.listNamespacedSecretCall(
kubernetesClientProperties.getNamespace(), null, null, null, null, null, null,
params.resourceVersion, params.timeoutSeconds, params.watch, null),
V1Secret.class, V1SecretList.class);
configMapInformer.addEventHandler(new ResourceEventHandler<V1Secret>() {
@Override
public void onAdd(V1Secret obj) {
LOG.info("Secret " + obj.getMetadata().getName() + " was added.");
onEvent(obj);
}
@Override
public void onUpdate(V1Secret oldObj, V1Secret newObj) {
LOG.info("Secret " + newObj.getMetadata().getName() + " was added.");
onEvent(newObj);
}
@Override
public void onDelete(V1Secret obj, boolean deletedFinalStateUnknown) {
LOG.info("Secret " + obj.getMetadata() + " was deleted.");
onEvent(obj);
}
});
factory.startAllRegisteredInformers();
}
}
private void onEvent(V1Secret secret) {
boolean changed = changed(locateMapPropertySources(this.propertySourceLocator, this.environment),
findPropertySources(KubernetesClientSecretsPropertySource.class));
if (changed) {
this.log.info("Detected change in secrets");
reloadProperties();
}
}
}

View File

@@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.client.config.reload.KubernetesClientConfigReloadAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.client.config.KubernetesClientBootstrapConfiguration

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import java.util.ArrayList;
import java.util.List;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.JSON;
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.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
class KubernetesClientConfigMapPropertySourceLocatorTests {
private static final V1ConfigMapList PROPERTIES_CONFIGMAP_LIST = new V1ConfigMapList()
.addItemsItem(
new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-640").withNamespace("default")
.withResourceVersion("1").build())
.addToData("application.properties",
"spring.cloud.kubernetes.configuration.watcher.refreshDelay=0\n"
+ "logging.level.org.springframework.cloud.kubernetes=TRACE")
.build());
private static final String API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor("localhost", wireMockServer.port());
ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
client.setDebugging(true);
Configuration.setDefaultApiClient(client);
}
@AfterAll
public static void after() {
wireMockServer.stop();
}
@AfterEach
public void afterEach() {
WireMock.reset();
}
@Test
void locateWithoutSources() {
CoreV1Api api = new CoreV1Api();
stubFor(get(API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST))));
ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties();
configMapConfigProperties.setName("bootstrap-640");
KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties();
kubernetesClientProperties.setNamespace("default");
PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(api,
configMapConfigProperties, kubernetesClientProperties).locate(new MockEnvironment());
assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isTrue();
}
@Test
void locateWithSources() {
CoreV1Api api = new CoreV1Api();
stubFor(get(API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST))));
ConfigMapConfigProperties configMapConfigProperties = new ConfigMapConfigProperties();
configMapConfigProperties.setName("fake-name");
ConfigMapConfigProperties.Source source1 = new ConfigMapConfigProperties.Source();
source1.setName("bootstrap-640");
source1.setNamespace("default");
List<ConfigMapConfigProperties.Source> sources = new ArrayList<>();
sources.add(source1);
configMapConfigProperties.setSources(sources);
KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties();
kubernetesClientProperties.setNamespace("dev");
PropertySource propertySource = new KubernetesClientConfigMapPropertySourceLocator(api,
configMapConfigProperties, kubernetesClientProperties).locate(new MockEnvironment());
assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isTrue();
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.JSON;
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.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
class KubernetesClientConfigMapPropertySourceTests {
private static final V1ConfigMapList PROPERTIES_CONFIGMAP_LIST = new V1ConfigMapList()
.addItemsItem(
new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-640").withNamespace("default")
.withResourceVersion("1").build())
.addToData("application.properties",
"spring.cloud.kubernetes.configuration.watcher.refreshDelay=0\n"
+ "logging.level.org.springframework.cloud.kubernetes=TRACE")
.build());
private static final V1ConfigMapList YAML_CONFIGMAP_LIST = new V1ConfigMapList()
.addItemsItem(new V1ConfigMapBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("bootstrap-641").withNamespace("default")
.withResourceVersion("1").build())
.addToData("application.yaml",
"dummy:\n property:\n string2: \"a\"\n int2: 1\n bool2: true\n")
.build());
private static final String API = "/api/v1/namespaces/default/configmaps";
private static WireMockServer wireMockServer;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor("localhost", wireMockServer.port());
ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
client.setDebugging(true);
Configuration.setDefaultApiClient(client);
}
@AfterAll
public static void after() {
wireMockServer.stop();
}
@AfterEach
public void afterEach() {
WireMock.reset();
}
@Test
public void propertiesFile() {
CoreV1Api api = new CoreV1Api();
stubFor(get(API)
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(PROPERTIES_CONFIGMAP_LIST))));
KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api,
"bootstrap-640", "default", new MockEnvironment());
verify(getRequestedFor(urlEqualTo(API)));
assertThat(propertySource.containsProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isTrue();
assertThat(propertySource.getProperty("spring.cloud.kubernetes.configuration.watcher.refreshDelay"))
.isEqualTo("0");
assertThat(propertySource.containsProperty("logging.level.org.springframework.cloud.kubernetes")).isTrue();
assertThat(propertySource.getProperty("logging.level.org.springframework.cloud.kubernetes")).isEqualTo("TRACE");
}
@Test
public void yamlFile() {
CoreV1Api api = new CoreV1Api();
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(YAML_CONFIGMAP_LIST))));
KubernetesClientConfigMapPropertySource propertySource = new KubernetesClientConfigMapPropertySource(api,
"bootstrap-641", "default", new MockEnvironment());
verify(getRequestedFor(urlEqualTo(API)));
assertThat(propertySource.containsProperty("dummy.property.string2")).isTrue();
assertThat(propertySource.getProperty("dummy.property.string2")).isEqualTo("a");
assertThat(propertySource.containsProperty("dummy.property.int2")).isTrue();
assertThat(propertySource.getProperty("dummy.property.int2")).isEqualTo(1);
assertThat(propertySource.containsProperty("dummy.property.bool2")).isTrue();
assertThat(propertySource.getProperty("dummy.property.bool2")).isEqualTo(true);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import java.util.ArrayList;
import java.util.List;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
class KubernetesClientSecretsPropertySourceLocatorTests {
private static final String LIST_API = "/api/v1/secrets";
private static final String LIST_API_WITH_LABEL = "/api/v1/secrets?labelSelector=spring.cloud.kubernetes.secret%3Dtrue";
private static final String LIST_BODY = "{\n" + "\t\"kind\": \"SecretList\",\n" + "\t\"apiVersion\": \"v1\",\n"
+ "\t\"metadata\": {\n" + "\t\t\"selfLink\": \"/api/v1/secrets\",\n"
+ "\t\t\"resourceVersion\": \"163035\"\n" + "\t},\n" + "\t\"items\": [{\n" + "\t\t\t\"metadata\": {\n"
+ "\t\t\t\t\"name\": \"db-secret\",\n" + "\t\t\t\t\"namespace\": \"default\",\n"
+ "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/db-secret\",\n"
+ "\t\t\t\t\"uid\": \"59ba8e6a-a2d4-416c-b016-22597c193f23\",\n"
+ "\t\t\t\t\"resourceVersion\": \"1462\",\n" + "\t\t\t\t\"creationTimestamp\": \"2020-10-28T14:45:02Z\",\n"
+ "\t\t\t\t\"labels\": {\n" + "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t}\n"
+ "\t\t\t},\n" + "\t\t\t\"data\": {\n" + "\t\t\t\t\"password\": \"cDQ1NXcwcmQ=\",\n"
+ "\t\t\t\t\"username\": \"dXNlcg==\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n" + "\t\t},\n"
+ "\t\t{\n" + "\t\t\t\"metadata\": {\n" + "\t\t\t\t\"name\": \"rabbit-password\",\n"
+ "\t\t\t\t\"namespace\": \"default\",\n"
+ "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/rabbit-password\",\n"
+ "\t\t\t\t\"uid\": \"bc211cb4-e7ff-4556-b26e-c54911301740\",\n"
+ "\t\t\t\t\"resourceVersion\": \"162708\",\n"
+ "\t\t\t\t\"creationTimestamp\": \"2020-10-29T19:47:36Z\",\n" + "\t\t\t\t\"labels\": {\n"
+ "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t},\n"
+ "\t\t\t\t\"annotations\": {\n"
+ "\t\t\t\t\t\"kubectl.kubernetes.io/last-applied-configuration\": \"{\\\"apiVersion\\\":\\\"v1\\\",\\\"data\\\":{\\\"spring.rabbitmq.password\\\":\\\"password\\\"},\\\"kind\\\":\\\"Secret\\\",\\\"metadata\\\":{\\\"annotations\\\":{},\\\"labels\\\":{\\\"spring.cloud.kubernetes.secret\\\":\\\"true\\\"},\\\"name\\\":\\\"rabbit-password\\\",\\\"namespace\\\":\\\"default\\\"},\\\"type\\\":\\\"Opaque\\\"}\\n\"\n"
+ "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"data\": {\n"
+ "\t\t\t\t\"spring.rabbitmq.password\": \"cGFzc3dvcmQ=\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n"
+ "\t\t}\n" + "\t]\n" + "}";
private static WireMockServer wireMockServer;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor("localhost", wireMockServer.port());
ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
client.setDebugging(true);
Configuration.setDefaultApiClient(client);
}
@AfterAll
public static void after() {
wireMockServer.stop();
}
@AfterEach
public void afterEach() {
WireMock.reset();
}
@Test
void getLocateWithSources() {
CoreV1Api api = new CoreV1Api();
stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY)));
SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties();
SecretsConfigProperties.Source source1 = new SecretsConfigProperties.Source();
source1.setName("db-secret");
source1.setNamespace("");
SecretsConfigProperties.Source source2 = new SecretsConfigProperties.Source();
source2.setName("rabbit-password");
source2.setNamespace("");
List<SecretsConfigProperties.Source> sources = new ArrayList<>();
sources.add(source1);
sources.add(source2);
secretsConfigProperties.setName("app");
secretsConfigProperties.setNamespace("");
secretsConfigProperties.setSources(sources);
secretsConfigProperties.setEnableApi(true);
PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(api,
new KubernetesClientProperties(), secretsConfigProperties).locate(new MockEnvironment());
assertThat(propertySource.containsProperty("password")).isTrue();
assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd");
}
@Test
void getLocateWithOutSources() {
CoreV1Api api = new CoreV1Api();
stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY)));
SecretsConfigProperties secretsConfigProperties = new SecretsConfigProperties();
secretsConfigProperties.setName("db-secret");
secretsConfigProperties.setNamespace("");
secretsConfigProperties.setEnableApi(true);
PropertySource propertySource = new KubernetesClientSecretsPropertySourceLocator(api,
new KubernetesClientProperties(), secretsConfigProperties).locate(new MockEnvironment());
assertThat(propertySource.containsProperty("password")).isTrue();
assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd");
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config;
import java.util.HashMap;
import java.util.Map;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.apis.CoreV1Api;
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 io.kubernetes.client.util.ClientBuilder;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.mock.env.MockEnvironment;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
*/
class KubernetesClientSecretsPropertySourceTests {
private static final String API = "/api/v1/namespaces/default/secrets";
private static final V1SecretList SECRET_LIST = new V1SecretListBuilder().addToItems(new V1SecretBuilder()
.withMetadata(new V1ObjectMetaBuilder().withName("db-secret").withResourceVersion("0")
.withNamespace("default").build())
.addToData("password", "p455w0rd".getBytes()).addToData("username", "user".getBytes()).build()).build();
private static final String LIST_API = "/api/v1/secrets";
private static final String LIST_API_WITH_LABEL = "/api/v1/secrets?labelSelector=spring.cloud.kubernetes.secret%3Dtrue";
private static final String LIST_BODY = "{\n" + "\t\"kind\": \"SecretList\",\n" + "\t\"apiVersion\": \"v1\",\n"
+ "\t\"metadata\": {\n" + "\t\t\"selfLink\": \"/api/v1/secrets\",\n"
+ "\t\t\"resourceVersion\": \"163035\"\n" + "\t},\n" + "\t\"items\": [{\n" + "\t\t\t\"metadata\": {\n"
+ "\t\t\t\t\"name\": \"db-secret\",\n" + "\t\t\t\t\"namespace\": \"default\",\n"
+ "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/db-secret\",\n"
+ "\t\t\t\t\"uid\": \"59ba8e6a-a2d4-416c-b016-22597c193f23\",\n"
+ "\t\t\t\t\"resourceVersion\": \"1462\",\n" + "\t\t\t\t\"creationTimestamp\": \"2020-10-28T14:45:02Z\",\n"
+ "\t\t\t\t\"labels\": {\n" + "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t}\n"
+ "\t\t\t},\n" + "\t\t\t\"data\": {\n" + "\t\t\t\t\"password\": \"cDQ1NXcwcmQ=\",\n"
+ "\t\t\t\t\"username\": \"dXNlcg==\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n" + "\t\t},\n"
+ "\t\t{\n" + "\t\t\t\"metadata\": {\n" + "\t\t\t\t\"name\": \"rabbit-password\",\n"
+ "\t\t\t\t\"namespace\": \"default\",\n"
+ "\t\t\t\t\"selfLink\": \"/api/v1/namespaces/default/secrets/rabbit-password\",\n"
+ "\t\t\t\t\"uid\": \"bc211cb4-e7ff-4556-b26e-c54911301740\",\n"
+ "\t\t\t\t\"resourceVersion\": \"162708\",\n"
+ "\t\t\t\t\"creationTimestamp\": \"2020-10-29T19:47:36Z\",\n" + "\t\t\t\t\"labels\": {\n"
+ "\t\t\t\t\t\"spring.cloud.kubernetes.secret\": \"true\"\n" + "\t\t\t\t},\n"
+ "\t\t\t\t\"annotations\": {\n"
+ "\t\t\t\t\t\"kubectl.kubernetes.io/last-applied-configuration\": \"{\\\"apiVersion\\\":\\\"v1\\\",\\\"data\\\":{\\\"spring.rabbitmq.password\\\":\\\"password\\\"},\\\"kind\\\":\\\"Secret\\\",\\\"metadata\\\":{\\\"annotations\\\":{},\\\"labels\\\":{\\\"spring.cloud.kubernetes.secret\\\":\\\"true\\\"},\\\"name\\\":\\\"rabbit-password\\\",\\\"namespace\\\":\\\"default\\\"},\\\"type\\\":\\\"Opaque\\\"}\\n\"\n"
+ "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"data\": {\n"
+ "\t\t\t\t\"spring.rabbitmq.password\": \"cGFzc3dvcmQ=\"\n" + "\t\t\t},\n" + "\t\t\t\"type\": \"Opaque\"\n"
+ "\t\t}\n" + "\t]\n" + "}";
private static WireMockServer wireMockServer;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor("localhost", wireMockServer.port());
ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
client.setDebugging(true);
Configuration.setDefaultApiClient(client);
}
@AfterAll
public static void after() {
wireMockServer.stop();
}
@AfterEach
public void afterEach() {
WireMock.reset();
}
@Test
public void secretsTest() {
CoreV1Api api = new CoreV1Api();
stubFor(get(API).willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(SECRET_LIST))));
KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(api,
"db-secret", "default", new MockEnvironment(), new HashMap<>());
assertThat(propertySource.containsProperty("password")).isTrue();
assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd");
assertThat(propertySource.containsProperty("username")).isTrue();
assertThat(propertySource.getProperty("username")).isEqualTo("user");
}
@Test
public void secretsNullNamespaceTest() {
CoreV1Api api = new CoreV1Api();
stubFor(get(LIST_API).willReturn(aResponse().withStatus(200).withBody(LIST_BODY)));
KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(api,
"db-secret", null, new MockEnvironment(), new HashMap<>());
assertThat(propertySource.containsProperty("password")).isTrue();
assertThat(propertySource.getProperty("password")).isEqualTo("p455w0rd");
assertThat(propertySource.containsProperty("username")).isTrue();
assertThat(propertySource.getProperty("username")).isEqualTo("user");
}
@Test
public void secretLabelsTest() {
CoreV1Api api = new CoreV1Api();
stubFor(get(LIST_API_WITH_LABEL).willReturn(aResponse().withStatus(200).withBody(LIST_BODY)));
Map<String, String> labels = new HashMap<>();
labels.put("spring.cloud.kubernetes.secret", "true");
KubernetesClientSecretsPropertySource propertySource = new KubernetesClientSecretsPropertySource(api, null,
null, new MockEnvironment(), labels);
assertThat(propertySource.containsProperty("spring.rabbitmq.password")).isTrue();
assertThat(propertySource.getProperty("spring.rabbitmq.password")).isEqualTo("password");
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config.reload;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.google.gson.Gson;
import io.kubernetes.client.informer.EventType;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1ConfigMap;
import io.kubernetes.client.openapi.models.V1ConfigMapList;
import io.kubernetes.client.openapi.models.V1ListMeta;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.Watch;
import okhttp3.OkHttpClient;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.mock.env.MockPropertySource;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Ryan Baxter
*/
class KubernetesClientEventBasedConfigMapChangeDetectorTests {
private static WireMockServer wireMockServer;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor("localhost", wireMockServer.port());
}
@AfterAll
public static void after() {
wireMockServer.stop();
}
@AfterEach
public void afterEach() {
WireMock.reset();
}
@Test
void watch() throws Exception {
Map<String, String> data = new HashMap<>();
data.put("application.properties", "spring.cloud.kubernetes.configuration.watcher.refreshDelay=0\n"
+ "logging.level.org.springframework.cloud.kubernetes=TRACE");
Map<String, String> updateData = new HashMap<>();
updateData.put("application.properties", "spring.cloud.kubernetes.configuration.watcher.refreshDelay=1\n"
+ "logging.level.org.springframework.cloud.kubernetes=TRACE");
V1ConfigMap applicationConfig = new V1ConfigMap().kind("ConfigMap")
.metadata(new V1ObjectMeta().namespace("default").name("bar1")).data(data);
V1ConfigMapList configMapList = new V1ConfigMapList().metadata(new V1ListMeta().resourceVersion("0"))
.items(Arrays.asList(applicationConfig));
stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch")
.whenScenarioStateIs(STARTED).withQueryParam("watch", equalTo("false"))
.willReturn(aResponse().withStatus(200).withBody(new Gson().toJson(configMapList)))
.willSetStateTo("update"));
Watch.Response<V1ConfigMap> watchResponse = new Watch.Response<>(EventType.MODIFIED.name(), new V1ConfigMap()
.kind("ConfigMap").metadata(new V1ObjectMeta().namespace("default").name("bar1")).data(updateData));
stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch")
.whenScenarioStateIs("update").withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(watchResponse)))
.willSetStateTo("add"));
stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch")
.whenScenarioStateIs("add").withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200)
.withBody(new JSON().serialize(new Watch.Response<>(EventType.ADDED.name(),
new V1ConfigMap().kind("ConfigMap")
.metadata(new V1ObjectMeta().namespace("default").name("bar3"))
.putDataItem("application.properties", "debug=true")))))
.willSetStateTo("delete"));
stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch")
.whenScenarioStateIs("delete").withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200)
.withBody(new JSON().serialize(new Watch.Response<>(EventType.DELETED.name(),
new V1ConfigMap().kind("ConfigMap")
.metadata(new V1ObjectMeta().namespace("default").name("bar1"))
.putDataItem("application.properties", "debug=true")))))
.willSetStateTo("done"));
stubFor(get(urlMatching("^/api/v1/namespaces/default/configmaps.*")).inScenario("watch")
.whenScenarioStateIs("done").withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200)));
ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
apiClient.setHttpClient(httpClient);
CoreV1Api coreV1Api = new CoreV1Api(apiClient);
ConfigurationUpdateStrategy strategy = mock(ConfigurationUpdateStrategy.class);
when(strategy.getName()).thenReturn("strategy");
KubernetesMockEnvironment environment = new KubernetesMockEnvironment(
mock(KubernetesClientConfigMapPropertySource.class)).withProperty("debug", "true");
KubernetesClientConfigMapPropertySourceLocator locator = mock(
KubernetesClientConfigMapPropertySourceLocator.class);
when(locator.locate(environment)).thenReturn(new MockPropertySource().withProperty("debug", "false"));
KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties();
kubernetesClientProperties.setNamespace("default");
KubernetesClientEventBasedConfigMapChangeDetector changeDetector = new KubernetesClientEventBasedConfigMapChangeDetector(
coreV1Api, environment, new ConfigReloadProperties(), strategy, locator, kubernetesClientProperties);
Thread controllerThread = new Thread(changeDetector::watch);
controllerThread.setDaemon(true);
controllerThread.start();
await().timeout(Duration.ofSeconds(5))
.until(() -> Mockito.mockingDetails(strategy).getInvocations().size() > 4);
verify(strategy, atLeast(3)).reload();
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config.reload;
import java.time.Duration;
import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.TimeUnit;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.google.gson.Gson;
import io.kubernetes.client.informer.EventType;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.JSON;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1ListMeta;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Secret;
import io.kubernetes.client.openapi.models.V1SecretList;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.Watch;
import okhttp3.OkHttpClient;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.mock.env.MockPropertySource;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;
import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Ryan Baxter
*/
class KubernetesClientEventBasedSecretsChangeDetectorTests {
private static WireMockServer wireMockServer;
@BeforeAll
public static void setup() {
wireMockServer = new WireMockServer(options().dynamicPort());
wireMockServer.start();
WireMock.configureFor("localhost", wireMockServer.port());
}
@AfterAll
public static void after() {
wireMockServer.stop();
}
@AfterEach
public void afterEach() {
WireMock.reset();
}
@Test
void watch() {
V1Secret dbPassword = new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("db-password"))
.putStringDataItem("password", Base64.getEncoder().encodeToString("p455w0rd".getBytes()))
.putStringDataItem("username", Base64.getEncoder().encodeToString("user".getBytes()));
V1Secret dbPasswordUpdated = new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("db-password"))
.putStringDataItem("password", Base64.getEncoder().encodeToString("p455w0rd2".getBytes()))
.putStringDataItem("username", Base64.getEncoder().encodeToString("user".getBytes()));
V1SecretList secretList = new V1SecretList().kind("SecretList").metadata(new V1ListMeta().resourceVersion("0"))
.items(Arrays.asList(dbPassword));
stubFor(get(urlMatching("^/api/v1/namespaces/default/secrets.*")).inScenario("watch")
.whenScenarioStateIs(STARTED).withQueryParam("watch", equalTo("false"))
.willReturn(aResponse().withStatus(200).withBody(new Gson().toJson(secretList)))
.willSetStateTo("update"));
Watch.Response<V1Secret> watchResponse = new Watch.Response<>(EventType.MODIFIED.name(), dbPasswordUpdated);
stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch")
.whenScenarioStateIs("update").withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200).withBody(new JSON().serialize(watchResponse)))
.willSetStateTo("add"));
stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch").whenScenarioStateIs("add")
.withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200)
.withBody(new JSON().serialize(new Watch.Response<>(EventType.ADDED.name(),
new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("rabbit-password"))
.putDataItem("rabbit-pw", Base64.getEncoder().encode("password".getBytes()))))))
.willSetStateTo("delete"));
stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch")
.whenScenarioStateIs("delete").withQueryParam("watch", equalTo("true"))
.willReturn(aResponse().withStatus(200)
.withBody(new JSON().serialize(new Watch.Response<>(EventType.DELETED.name(),
new V1Secret().kind("Secret").metadata(new V1ObjectMeta().name("rabbit-password"))
.putDataItem("rabbit-pw", Base64.getEncoder().encode("password".getBytes()))))))
.willSetStateTo("done"));
stubFor(get(urlMatching("/api/v1/namespaces/default/secrets.*")).inScenario("watch").whenScenarioStateIs("done")
.withQueryParam("watch", equalTo("true")).willReturn(aResponse().withStatus(200)));
ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
OkHttpClient httpClient = apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
apiClient.setHttpClient(httpClient);
CoreV1Api coreV1Api = new CoreV1Api(apiClient);
ConfigurationUpdateStrategy strategy = mock(ConfigurationUpdateStrategy.class);
when(strategy.getName()).thenReturn("strategy");
KubernetesMockEnvironment environment = new KubernetesMockEnvironment(
mock(KubernetesClientSecretsPropertySource.class)).withProperty("db-password", "p455w0rd");
KubernetesClientSecretsPropertySourceLocator locator = mock(KubernetesClientSecretsPropertySourceLocator.class);
when(locator.locate(environment)).thenReturn(new MockPropertySource().withProperty("db-password", "p455w0rd2"));
ConfigReloadProperties properties = new ConfigReloadProperties();
properties.setMonitoringSecrets(true);
KubernetesClientProperties kubernetesClientProperties = new KubernetesClientProperties();
kubernetesClientProperties.setNamespace("default");
KubernetesClientEventBasedSecretsChangeDetector changeDetector = new KubernetesClientEventBasedSecretsChangeDetector(
coreV1Api, environment, properties, strategy, locator, kubernetesClientProperties);
Thread controllerThread = new Thread(changeDetector::watch);
controllerThread.setDaemon(true);
controllerThread.start();
await().timeout(Duration.ofSeconds(300))
.until(() -> Mockito.mockingDetails(strategy).getInvocations().size() > 4);
verify(strategy, atLeast(3)).reload();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.client.config.reload;
import java.util.HashMap;
import java.util.Map;
import org.springframework.cloud.kubernetes.client.config.KubernetesClientSecretsPropertySource;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.PropertySource;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Ryan Baxter
*/
public class KubernetesMockEnvironment extends AbstractEnvironment {
private PropertySource propertySource = mock(KubernetesClientSecretsPropertySource.class);
private Map<String, Object> map = new HashMap<>();
public KubernetesMockEnvironment(PropertySource mockPropertySource) {
this.propertySource = mockPropertySource;
this.getPropertySources().addLast(this.propertySource);
when(propertySource.getSource()).thenReturn(map);
}
public void setProperty(String key, String value) {
map.put(key, value);
when(propertySource.getProperty(eq(key))).thenReturn(value);
}
public KubernetesMockEnvironment withProperty(String key, String value) {
this.setProperty(key, value);
return this;
}
}

View File

@@ -26,6 +26,14 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
/**
* Abstraction over configuration properties.

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
import java.util.Collections;
import java.util.List;
@@ -137,7 +137,7 @@ public class ConfigMapConfigProperties extends AbstractConfigProperties {
}
static class NormalizedSource {
public static class NormalizedSource {
private final String name;

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.commons.config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.throwingMerger;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.yamlParserGenerator;
/**
* A {@link MapPropertySource} that uses Kubernetes config maps.
*
* @author Ioannis Canellos
* @author Ali Shahbour
* @author Michael Moudatsos
*/
public abstract class ConfigMapPropertySource extends MapPropertySource {
private static final Log LOG = LogFactory.getLog(ConfigMapPropertySource.class);
protected static final String APPLICATION_YML = "application.yml";
protected static final String APPLICATION_YAML = "application.yaml";
protected static final String APPLICATION_PROPERTIES = "application.properties";
protected static final String PREFIX = "configmap";
public ConfigMapPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
protected static Environment createEnvironmentWithActiveProfiles(String[] activeProfiles) {
StandardEnvironment environment = new StandardEnvironment();
environment.setActiveProfiles(activeProfiles);
return environment;
}
protected static String getName(String name, String namespace) {
return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name)
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(namespace).toString();
}
protected static Map<String, Object> processAllEntries(Map<String, String> input, Environment environment) {
Set<Entry<String, String>> entrySet = input.entrySet();
if (entrySet.size() == 1) {
// we handle the case where the configmap contains a single "file"
// in this case we don't care what the name of t he file is
Entry<String, String> singleEntry = entrySet.iterator().next();
String propertyName = singleEntry.getKey();
String propertyValue = singleEntry.getValue();
if (propertyName.endsWith(".yml") || propertyName.endsWith(".yaml")) {
if (LOG.isDebugEnabled()) {
LOG.debug("The single property with name: [" + propertyName + "] will be treated as a yaml file");
}
return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(propertyValue);
}
else if (propertyName.endsWith(".properties")) {
if (LOG.isDebugEnabled()) {
LOG.debug("The single property with name: [" + propertyName
+ "] will be treated as a properties file");
}
return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(propertyValue);
}
else {
return defaultProcessAllEntries(input, environment);
}
}
return defaultProcessAllEntries(input, environment);
}
protected static Map<String, Object> defaultProcessAllEntries(Map<String, String> input, Environment environment) {
return input.entrySet().stream().map(e -> extractProperties(e.getKey(), e.getValue(), environment))
.filter(m -> !m.isEmpty()).flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, throwingMerger(), LinkedHashMap::new));
}
protected static Map<String, Object> extractProperties(String resourceName, String content,
Environment environment) {
if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) {
return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(content);
}
else if (resourceName.equals(APPLICATION_PROPERTIES)) {
return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content);
}
return new LinkedHashMap<String, Object>() {
{
put(resourceName, content);
}
};
}
protected static Map<String, Object> asObjectMap(Map<String, Object> source) {
return source.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, throwingMerger(), LinkedHashMap::new));
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
import java.io.IOException;
import java.nio.file.Files;
@@ -22,26 +22,23 @@ import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import io.fabric8.kubernetes.api.builder.Function;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.kubernetes.config.ConfigMapConfigProperties.NormalizedSource;
import org.springframework.core.annotation.Order;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties.NormalizedSource;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator;
import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.commons.config.PropertySourceUtils.yamlParserGenerator;
/**
* A {@link PropertySourceLocator} that uses config maps.
@@ -49,17 +46,13 @@ import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.ya
* @author Ioannis Canellos
* @author Michael Moudatsos
*/
@Order(0)
public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
public abstract class ConfigMapPropertySourceLocator implements PropertySourceLocator {
private static final Log LOG = LogFactory.getLog(ConfigMapPropertySourceLocator.class);
private final KubernetesClient client;
protected final ConfigMapConfigProperties properties;
private final ConfigMapConfigProperties properties;
public ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties) {
this.client = client;
public ConfigMapPropertySourceLocator(ConfigMapConfigProperties properties) {
this.properties = properties;
}
@@ -68,7 +61,7 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
if (environment instanceof ConfigurableEnvironment) {
ConfigurableEnvironment env = (ConfigurableEnvironment) environment;
List<ConfigMapConfigProperties.NormalizedSource> sources = this.properties.determineSources();
List<NormalizedSource> sources = this.properties.determineSources();
CompositePropertySource composite = new CompositePropertySource("composite-configmap");
if (this.properties.isEnableApi()) {
sources.forEach(s -> composite.addFirstPropertySource(getMapPropertySourceForSingleConfigMap(env, s)));
@@ -85,12 +78,13 @@ public class ConfigMapPropertySourceLocator implements PropertySourceLocator {
NormalizedSource normalizedSource) {
String configurationTarget = this.properties.getConfigurationTarget();
return new ConfigMapPropertySource(this.client,
getApplicationName(environment, normalizedSource.getName(), configurationTarget),
getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget),
environment);
return getMapPropertySource(getApplicationName(environment, normalizedSource.getName(), configurationTarget),
normalizedSource, configurationTarget, environment);
}
protected abstract MapPropertySource getMapPropertySource(String name, NormalizedSource normalizedSource,
String configurationTarget, ConfigurableEnvironment environment);
private void addPropertySourcesFromPaths(Environment environment, CompositePropertySource composite) {
this.properties.getPaths().stream().map(Paths::get).peek(p -> {
if (!Files.exists(p)) {

View File

@@ -14,17 +14,16 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.config.Constants.FALLBACK_APPLICATION_NAME;
import static org.springframework.cloud.kubernetes.config.Constants.SPRING_APPLICATION_NAME;
import static org.springframework.cloud.kubernetes.commons.config.Constants.FALLBACK_APPLICATION_NAME;
import static org.springframework.cloud.kubernetes.commons.config.Constants.SPRING_APPLICATION_NAME;
/**
* Utility class that works with configuration properties.
@@ -36,7 +35,6 @@ public final class ConfigUtils {
private static final Log LOG = LogFactory.getLog(ConfigUtils.class);
private ConfigUtils() {
throw new IllegalStateException("Can't instantiate a utility class");
}
public static String getApplicationName(Environment env, String configName, String configurationTarget) {
@@ -50,15 +48,4 @@ public final class ConfigUtils {
return configName;
}
public static String getApplicationNamespace(KubernetesClient client, String configNamespace,
String configurationTarget) {
if (StringUtils.isEmpty(configNamespace)) {
LOG.debug(configurationTarget + " namespace has not been set, taking it from client (ns="
+ client.getNamespace() + ")");
configNamespace = client.getNamespace();
}
return configNamespace;
}
}

View File

@@ -14,13 +14,24 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
final class Constants {
public final class Constants {
static final String SPRING_APPLICATION_NAME = "spring.application.name";
static final String FALLBACK_APPLICATION_NAME = "application";
static final String PROPERTY_SOURCE_NAME_SEPARATOR = ".";
/**
* Spring application name property.
*/
public static final String SPRING_APPLICATION_NAME = "spring.application.name";
/**
* Default application name.
*/
public static final String FALLBACK_APPLICATION_NAME = "application";
/**
* Property separator.
*/
public static final String PROPERTY_SOURCE_NAME_SEPARATOR = ".";
private Constants() {
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2013-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.commons.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.context.annotation.Configuration;
/**
* @author Ryan Baxter
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnKubernetesEnabled
@EnableConfigurationProperties({ ConfigMapConfigProperties.class, SecretsConfigProperties.class })
public class KubernetesBootstrapConfiguration {
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -42,7 +42,10 @@ import static org.springframework.beans.factory.config.YamlProcessor.MatchStatus
*/
public final class PropertySourceUtils {
static final Function<String, Properties> KEY_VALUE_TO_PROPERTIES = s -> {
/**
* Function to convert a String to Properties.
*/
public static final Function<String, Properties> KEY_VALUE_TO_PROPERTIES = s -> {
Properties properties = new Properties();
try {
properties.load(new ByteArrayInputStream(s.getBytes()));
@@ -52,7 +55,11 @@ public final class PropertySourceUtils {
throw new IllegalArgumentException();
}
};
static final Function<Properties, Map<String, Object>> PROPERTIES_TO_MAP = p -> p.entrySet().stream()
/**
* Function to convert Properties to a Map.
*/
public static final Function<Properties, Map<String, Object>> PROPERTIES_TO_MAP = p -> p.entrySet().stream()
.collect(Collectors.toMap(e -> String.valueOf(e.getKey()), Map.Entry::getValue, throwingMerger(),
java.util.LinkedHashMap::new));
@@ -60,7 +67,12 @@ public final class PropertySourceUtils {
throw new IllegalStateException("Can't instantiate a utility class");
}
static Function<String, Properties> yamlParserGenerator(Environment environment) {
/**
* Function to convert String into Properties with an environment.
* @param environment Environment.
* @return properties.
*/
public static Function<String, Properties> yamlParserGenerator(Environment environment) {
return s -> {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setDocumentMatchers(properties -> {
@@ -77,7 +89,12 @@ public final class PropertySourceUtils {
};
}
static <T> BinaryOperator<T> throwingMerger() {
/**
* Throws IllegalStateException.
* @param <T> Throwable.
* @return IllegalStateException.
*/
public static <T> BinaryOperator<T> throwingMerger() {
return (u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
};

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
import java.util.ArrayList;
import java.util.HashMap;
@@ -169,7 +169,7 @@ public class SecretsConfigProperties extends AbstractConfigProperties {
}
static class NormalizedSource {
public static class NormalizedSource {
private final String name;

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.commons.config;
import java.util.Base64;
import java.util.Map;
import org.springframework.core.env.MapPropertySource;
/**
* Kubernetes property source for secrets.
*
* @author l burgazzoli
* @author Haytham Mohamed
*/
public class SecretsPropertySource extends MapPropertySource {
private static final String PREFIX = "secrets";
public SecretsPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
protected static String getSourceName(String name, String namespace) {
return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name)
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(namespace).toString();
}
protected static void putAll(Map<String, String> data, Map<String, Object> result) {
if (data != null) {
data.forEach((k, v) -> result.put(k, new String(Base64.getDecoder().decode(v)).trim()));
}
}
@Override
public String toString() {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.commons.config;
import java.io.IOException;
import java.nio.file.Files;
@@ -24,38 +24,29 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationName;
import static org.springframework.cloud.kubernetes.config.ConfigUtils.getApplicationNamespace;
/**
* Kubernetes {@link PropertySourceLocator} for secrets.
*
* @author l burgazzoli
* @author Haytham Mohamed
*/
@Order(1)
public class SecretsPropertySourceLocator implements PropertySourceLocator {
public abstract class SecretsPropertySourceLocator implements PropertySourceLocator {
private static final Log LOG = LogFactory.getLog(SecretsPropertySourceLocator.class);
private final KubernetesClient client;
protected final SecretsConfigProperties properties;
private final SecretsConfigProperties properties;
public SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties) {
this.client = client;
public SecretsPropertySourceLocator(SecretsConfigProperties properties) {
this.properties = properties;
}
@@ -83,17 +74,17 @@ public class SecretsPropertySourceLocator implements PropertySourceLocator {
SecretsConfigProperties.NormalizedSource normalizedSource) {
String configurationTarget = this.properties.getConfigurationTarget();
return new SecretsPropertySource(this.client, environment,
getApplicationName(environment, normalizedSource.getName(), configurationTarget),
getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget),
normalizedSource.getLabels());
return getPropertySource(environment, normalizedSource, configurationTarget);
}
private void putPathConfig(CompositePropertySource composite) {
protected abstract MapPropertySource getPropertySource(ConfigurableEnvironment environment,
SecretsConfigProperties.NormalizedSource normalizedSource, String configurationTarget);
protected void putPathConfig(CompositePropertySource composite) {
this.properties.getPaths().stream().map(Paths::get).filter(Files::exists).forEach(p -> putAll(p, composite));
}
private void putAll(Path path, CompositePropertySource composite) {
protected void putAll(Path path, CompositePropertySource composite) {
try {
Files.walk(path).filter(Files::isRegularFile).forEach(p -> readFile(p, composite));
@@ -103,7 +94,7 @@ public class SecretsPropertySourceLocator implements PropertySourceLocator {
}
}
private void readFile(Path path, CompositePropertySource composite) {
protected void readFile(Path path, CompositePropertySource composite) {
try {
Map<String, Object> result = new HashMap<>();
result.put(path.getFileName().toString(), new String(Files.readAllBytes(path)).trim());

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.commons.config.reload;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.cloud.kubernetes.commons.ConditionalOnKubernetesEnabled;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
/**
* @author Ryan Baxter
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnKubernetesEnabled
@ConditionalOnClass(EndpointAutoConfiguration.class)
@AutoConfigureAfter({ InfoEndpointAutoConfiguration.class, RefreshEndpointAutoConfiguration.class,
RefreshAutoConfiguration.class })
public class ConfigReloadAutoConfiguration {
/**
* Configuration reload must be enabled explicitly.
*/
@ConditionalOnProperty("spring.cloud.kubernetes.reload.enabled")
@ConditionalOnClass({ RestartEndpoint.class, ContextRefresher.class })
protected static class ConfigReloadAutoConfigurationBeans {
/**
* @param properties config reload properties
* @param ctx application context
* @param restarter restart endpoint
* @param refresher context refresher
* @return provides the action to execute when the configuration changes.
*/
@Bean
@ConditionalOnMissingBean
public ConfigurationUpdateStrategy configurationUpdateStrategy(ConfigReloadProperties properties,
ConfigurableApplicationContext ctx, @Autowired(required = false) RestartEndpoint restarter,
ContextRefresher refresher) {
switch (properties.getStrategy()) {
case RESTART_CONTEXT:
Assert.notNull(restarter, "Restart endpoint is not enabled");
return new ConfigurationUpdateStrategy(properties.getStrategy().name(), () -> {
wait(properties);
restarter.restart();
});
case REFRESH:
return new ConfigurationUpdateStrategy(properties.getStrategy().name(), refresher::refresh);
case SHUTDOWN:
return new ConfigurationUpdateStrategy(properties.getStrategy().name(), () -> {
wait(properties);
ctx.close();
});
}
throw new IllegalStateException("Unsupported configuration update strategy: " + properties.getStrategy());
}
private static void wait(ConfigReloadProperties properties) {
final long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis());
try {
Thread.sleep(waitMillis);
}
catch (InterruptedException ignored) {
}
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.commons.config.reload;
import java.time.Duration;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.commons.config.reload;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -23,9 +23,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.annotation.PreDestroy;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -51,25 +48,15 @@ public abstract class ConfigurationChangeDetector {
protected ConfigReloadProperties properties;
protected KubernetesClient kubernetesClient;
protected ConfigurationUpdateStrategy strategy;
public ConfigurationChangeDetector(ConfigurableEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy) {
ConfigurationUpdateStrategy strategy) {
this.environment = environment;
this.properties = properties;
this.kubernetesClient = kubernetesClient;
this.strategy = strategy;
}
@PreDestroy
public void shutdown() {
// Ensure the kubernetes client is cleaned up from spare threads when shutting
// down
this.kubernetesClient.close();
}
public void reloadProperties() {
this.log.info("Reloading using strategy: " + this.strategy.getName());
this.strategy.reload();
@@ -81,7 +68,7 @@ public abstract class ConfigurationChangeDetector {
* @param right right map property sources
* @return {@code true} if source has changed
*/
protected boolean changed(MapPropertySource left, MapPropertySource right) {
public boolean changed(MapPropertySource left, MapPropertySource right) {
if (left == right) {
return false;
}
@@ -93,7 +80,7 @@ public abstract class ConfigurationChangeDetector {
return !Objects.equals(leftMap, rightMap);
}
protected boolean changed(List<? extends MapPropertySource> left, List<? extends MapPropertySource> right) {
public boolean changed(List<? extends MapPropertySource> left, List<? extends MapPropertySource> right) {
if (left.size() != right.size()) {
this.log.warn("The current number of ConfigMap PropertySources does not match "
@@ -132,7 +119,7 @@ public abstract class ConfigurationChangeDetector {
* @param sourceClass class for which property sources will be found
* @return finds all registered property sources of the given type
*/
protected <S extends PropertySource<?>> List<S> findPropertySources(Class<S> sourceClass) {
public <S extends PropertySource<?>> List<S> findPropertySources(Class<S> sourceClass) {
List<S> managedSources = new LinkedList<>();
LinkedList<PropertySource<?>> sources = toLinkedList(this.environment.getPropertySources());

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.commons.config.reload;
import java.util.Objects;

View File

@@ -14,18 +14,16 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.commons.config.reload;
import java.util.List;
import javax.annotation.PostConstruct;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.scheduling.annotation.Scheduled;
@@ -42,14 +40,16 @@ public class PollingConfigMapChangeDetector extends ConfigurationChangeDetector
protected Log log = LogFactory.getLog(getClass());
private ConfigMapPropertySourceLocator configMapPropertySourceLocator;
private PropertySourceLocator propertySourceLocator;
private Class propertySourceClass;
public PollingConfigMapChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
super(environment, properties, kubernetesClient, strategy);
this.configMapPropertySourceLocator = configMapPropertySourceLocator;
ConfigurationUpdateStrategy strategy, Class propertySourceClass,
PropertySourceLocator propertySourceLocator) {
super(environment, properties, strategy);
this.propertySourceLocator = propertySourceLocator;
this.propertySourceClass = propertySourceClass;
}
@PostConstruct
@@ -66,12 +66,10 @@ public class PollingConfigMapChangeDetector extends ConfigurationChangeDetector
if (log.isDebugEnabled()) {
log.debug("Polling for changes in config maps");
}
List<? extends MapPropertySource> currentConfigMapSources = findPropertySources(
ConfigMapPropertySource.class);
List<? extends MapPropertySource> currentConfigMapSources = findPropertySources(propertySourceClass);
if (!currentConfigMapSources.isEmpty()) {
changedConfigMap = changed(
locateMapPropertySources(this.configMapPropertySourceLocator, this.environment),
changedConfigMap = changed(locateMapPropertySources(this.propertySourceLocator, this.environment),
currentConfigMapSources);
}
}

View File

@@ -14,18 +14,16 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.commons.config.reload;
import java.util.List;
import javax.annotation.PostConstruct;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.config.SecretsPropertySource;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.scheduling.annotation.Scheduled;
@@ -42,14 +40,16 @@ public class PollingSecretsChangeDetector extends ConfigurationChangeDetector {
protected Log log = LogFactory.getLog(getClass());
private final SecretsPropertySourceLocator secretsPropertySourceLocator;
private final PropertySourceLocator propertySourceLocator;
private Class propertySourceClass;
public PollingSecretsChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
SecretsPropertySourceLocator secretsPropertySourceLocator) {
super(environment, properties, kubernetesClient, strategy);
this.secretsPropertySourceLocator = secretsPropertySourceLocator;
ConfigurationUpdateStrategy strategy, Class propertySourceClass,
PropertySourceLocator propertySourceLocator) {
super(environment, properties, strategy);
this.propertySourceClass = propertySourceClass;
this.propertySourceLocator = propertySourceLocator;
}
@PostConstruct
@@ -66,10 +66,10 @@ public class PollingSecretsChangeDetector extends ConfigurationChangeDetector {
if (log.isDebugEnabled()) {
log.debug("Polling for changes in secrets");
}
List<MapPropertySource> currentSecretSources = locateMapPropertySources(this.secretsPropertySourceLocator,
List<MapPropertySource> currentSecretSources = locateMapPropertySources(this.propertySourceLocator,
this.environment);
if (currentSecretSources != null && !currentSecretSources.isEmpty()) {
List<SecretsPropertySource> propertySources = findPropertySources(SecretsPropertySource.class);
List<MapPropertySource> propertySources = findPropertySources(this.propertySourceClass);
changedSecrets = changed(currentSecretSources, propertySources);
}
}

View File

@@ -14,9 +14,9 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload.condition;
package org.springframework.cloud.kubernetes.commons.config.reload.condition;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties.ReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
@@ -38,7 +38,7 @@ public class EventReloadDetectionMode implements Condition {
}
else {
if (environment.getProperty("spring.cloud.kubernetes.reload.mode")
.equalsIgnoreCase(ReloadDetectionMode.EVENT.name())) {
.equalsIgnoreCase(ConfigReloadProperties.ReloadDetectionMode.EVENT.name())) {
return true;
}
}

View File

@@ -14,9 +14,9 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload.condition;
package org.springframework.cloud.kubernetes.commons.config.reload.condition;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties.ReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
@@ -38,7 +38,7 @@ public class PollingReloadDetectionMode implements Condition {
}
else {
if (environment.getProperty("spring.cloud.kubernetes.reload.mode")
.equalsIgnoreCase(ReloadDetectionMode.POLLING.name())) {
.equalsIgnoreCase(ConfigReloadProperties.ReloadDetectionMode.POLLING.name())) {
return true;
}
}

View File

@@ -1,2 +1,5 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration
org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration,\
org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration

View File

@@ -1,180 +0,0 @@
/*
* Copyright 2013-2019 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.config;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.StringUtils;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.KEY_VALUE_TO_PROPERTIES;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.PROPERTIES_TO_MAP;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.throwingMerger;
import static org.springframework.cloud.kubernetes.config.PropertySourceUtils.yamlParserGenerator;
/**
* A {@link MapPropertySource} that uses Kubernetes config maps.
*
* @author Ioannis Canellos
* @author Ali Shahbour
* @author Michael Moudatsos
*/
public class ConfigMapPropertySource extends MapPropertySource {
private static final Log LOG = LogFactory.getLog(ConfigMapPropertySource.class);
private static final String APPLICATION_YML = "application.yml";
private static final String APPLICATION_YAML = "application.yaml";
private static final String APPLICATION_PROPERTIES = "application.properties";
private static final String PREFIX = "configmap";
public ConfigMapPropertySource(KubernetesClient client, String name) {
this(client, name, null, (Environment) null);
}
public ConfigMapPropertySource(KubernetesClient client, String name, String namespace, String[] profiles) {
this(client, name, namespace, createEnvironmentWithActiveProfiles(profiles));
}
private static Environment createEnvironmentWithActiveProfiles(String[] activeProfiles) {
StandardEnvironment environment = new StandardEnvironment();
environment.setActiveProfiles(activeProfiles);
return environment;
}
public ConfigMapPropertySource(KubernetesClient client, String name, String namespace, Environment environment) {
super(getName(client, name, namespace), asObjectMap(getData(client, name, namespace, environment)));
}
private static String getName(KubernetesClient client, String name, String namespace) {
return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name)
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR)
.append(namespace == null || namespace.isEmpty() ? client.getNamespace() : namespace).toString();
}
private static Map<String, Object> getData(KubernetesClient client, String name, String namespace,
Environment environment) {
try {
Map<String, Object> result = new LinkedHashMap<>();
ConfigMap map = StringUtils.isEmpty(namespace) ? client.configMaps().withName(name).get()
: client.configMaps().inNamespace(namespace).withName(name).get();
if (map != null) {
result.putAll(processAllEntries(map.getData(), environment));
}
if (environment != null) {
for (String activeProfile : environment.getActiveProfiles()) {
String mapNameWithProfile = name + "-" + activeProfile;
ConfigMap mapWithProfile = StringUtils.isEmpty(namespace)
? client.configMaps().withName(mapNameWithProfile).get()
: client.configMaps().inNamespace(namespace).withName(mapNameWithProfile).get();
if (mapWithProfile != null) {
result.putAll(processAllEntries(mapWithProfile.getData(), environment));
}
}
}
return result;
}
catch (Exception e) {
LOG.warn("Can't read configMap with name: [" + name + "] in namespace:[" + namespace + "]. Ignoring.", e);
}
return new LinkedHashMap<>();
}
private static Map<String, Object> processAllEntries(Map<String, String> input, Environment environment) {
Set<Entry<String, String>> entrySet = input.entrySet();
if (entrySet.size() == 1) {
// we handle the case where the configmap contains a single "file"
// in this case we don't care what the name of t he file is
Entry<String, String> singleEntry = entrySet.iterator().next();
String propertyName = singleEntry.getKey();
String propertyValue = singleEntry.getValue();
if (propertyName.endsWith(".yml") || propertyName.endsWith(".yaml")) {
if (LOG.isDebugEnabled()) {
LOG.debug("The single property with name: [" + propertyName + "] will be treated as a yaml file");
}
return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(propertyValue);
}
else if (propertyName.endsWith(".properties")) {
if (LOG.isDebugEnabled()) {
LOG.debug("The single property with name: [" + propertyName
+ "] will be treated as a properties file");
}
return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(propertyValue);
}
else {
return defaultProcessAllEntries(input, environment);
}
}
return defaultProcessAllEntries(input, environment);
}
private static Map<String, Object> defaultProcessAllEntries(Map<String, String> input, Environment environment) {
return input.entrySet().stream().map(e -> extractProperties(e.getKey(), e.getValue(), environment))
.filter(m -> !m.isEmpty()).flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, throwingMerger(), LinkedHashMap::new));
}
private static Map<String, Object> extractProperties(String resourceName, String content, Environment environment) {
if (resourceName.equals(APPLICATION_YAML) || resourceName.equals(APPLICATION_YML)) {
return yamlParserGenerator(environment).andThen(PROPERTIES_TO_MAP).apply(content);
}
else if (resourceName.equals(APPLICATION_PROPERTIES)) {
return KEY_VALUE_TO_PROPERTIES.andThen(PROPERTIES_TO_MAP).apply(content);
}
return new LinkedHashMap<String, Object>() {
{
put(resourceName, content);
}
};
}
private static Map<String, Object> asObjectMap(Map<String, Object> source) {
return source.entrySet().stream().collect(
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, throwingMerger(), LinkedHashMap::new));
}
}

View File

@@ -1,4 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.config.reload.ConfigReloadAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.config.BootstrapConfiguration

View File

@@ -22,9 +22,9 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.env.AbstractEnvironment;
@@ -43,10 +43,10 @@ public class BusEventBasedConfigMapWatcherChangeDetector extends ConfigMapWatche
public BusEventBasedConfigMapWatcherChangeDetector(AbstractEnvironment environment,
ConfigReloadProperties properties, KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
ConfigMapPropertySourceLocator configMapPropertySourceLocator, BusProperties busProperties,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator, BusProperties busProperties,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
super(environment, properties, kubernetesClient, strategy, configMapPropertySourceLocator,
super(environment, properties, kubernetesClient, strategy, fabric8ConfigMapPropertySourceLocator,
k8SConfigurationProperties, threadPoolTaskExecutor);
this.busProperties = busProperties;

View File

@@ -22,9 +22,9 @@ import reactor.core.publisher.Mono;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.env.AbstractEnvironment;
@@ -43,10 +43,10 @@ public class BusEventBasedSecretsWatcherChangeDetector extends SecretsWatcherCha
public BusEventBasedSecretsWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
SecretsPropertySourceLocator secretsPropertySourceLocator, BusProperties busProperties,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, BusProperties busProperties,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
super(environment, properties, kubernetesClient, strategy, secretsPropertySourceLocator,
super(environment, properties, kubernetesClient, strategy, fabric8SecretsPropertySourceLocator,
k8SConfigurationProperties, threadPoolTaskExecutor);
this.busProperties = busProperties;

View File

@@ -26,10 +26,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.config.reload.EventBasedConfigMapChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.reload.EventBasedConfigMapChangeDetector;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -47,10 +47,10 @@ public abstract class ConfigMapWatcherChangeDetector extends EventBasedConfigMap
public ConfigMapWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
ConfigMapPropertySourceLocator configMapPropertySourceLocator,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
super(environment, properties, kubernetesClient, strategy, configMapPropertySourceLocator);
super(environment, properties, kubernetesClient, strategy, fabric8ConfigMapPropertySourceLocator);
this.executorService = Executors.newScheduledThreadPool(k8SConfigurationProperties.getThreadPoolSize(),
threadPoolTaskExecutor);
this.k8SConfigurationProperties = k8SConfigurationProperties;

View File

@@ -23,11 +23,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -53,27 +53,28 @@ public class ConfigurationWatcherAutoConfiguration {
@Bean
@ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class)
public ConfigMapWatcherChangeDetector httpBasedConfigMapWatchChangeDetector(AbstractEnvironment environment,
KubernetesClient kubernetesClient, ConfigMapPropertySourceLocator configMapPropertySourceLocator,
SecretsPropertySourceLocator secretsPropertySourceLocator, ConfigReloadProperties properties,
KubernetesClient kubernetesClient,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator, ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadFactory, WebClient webClient,
KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient) {
return new HttpBasedConfigMapWatchChangeDetector(environment, properties, kubernetesClient, strategy,
configMapPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient,
fabric8ConfigMapPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient,
kubernetesReactiveDiscoveryClient);
}
@Bean
@ConditionalOnMissingBean(SecretsWatcherChangeDetector.class)
public SecretsWatcherChangeDetector httpBasedSecretsWatchChangeDetector(AbstractEnvironment environment,
KubernetesClient kubernetesClient, SecretsPropertySourceLocator secretsPropertySourceLocator,
KubernetesClient kubernetesClient, Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadFactory, WebClient webClient,
KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient) {
return new HttpBasedSecretsWatchChangeDetector(environment, properties, kubernetesClient, strategy,
secretsPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient,
fabric8SecretsPropertySourceLocator, k8SConfigurationProperties, threadFactory, webClient,
kubernetesReactiveDiscoveryClient);
}
@@ -86,24 +87,24 @@ public class ConfigurationWatcherAutoConfiguration {
@ConditionalOnMissingBean(ConfigMapWatcherChangeDetector.class)
public ConfigMapWatcherChangeDetector busConfigMapChangeWatcher(BusProperties busProperties,
AbstractEnvironment environment, KubernetesClient kubernetesClient,
ConfigMapPropertySourceLocator configMapPropertySourceLocator, ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadFactory) {
return new BusEventBasedConfigMapWatcherChangeDetector(environment, properties, kubernetesClient, strategy,
configMapPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory);
fabric8ConfigMapPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory);
}
@Bean
@ConditionalOnMissingBean(SecretsWatcherChangeDetector.class)
public SecretsWatcherChangeDetector busSecretsChangeWatcher(BusProperties busProperties,
AbstractEnvironment environment, KubernetesClient kubernetesClient,
SecretsPropertySourceLocator secretsPropertySourceLocator, ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator,
ConfigReloadProperties properties, ConfigurationUpdateStrategy strategy,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadFactory) {
return new BusEventBasedSecretsWatcherChangeDetector(environment, properties, kubernetesClient, strategy,
secretsPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory);
fabric8SecretsPropertySourceLocator, busProperties, k8SConfigurationProperties, threadFactory);
}
}

View File

@@ -27,10 +27,10 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -57,11 +57,11 @@ public class HttpBasedConfigMapWatchChangeDetector extends ConfigMapWatcherChang
public HttpBasedConfigMapWatchChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
ConfigMapPropertySourceLocator configMapPropertySourceLocator,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadPoolTaskExecutor, WebClient webClient,
KubernetesReactiveDiscoveryClient k8sReactiveDiscoveryClient) {
super(environment, properties, kubernetesClient, strategy, configMapPropertySourceLocator,
super(environment, properties, kubernetesClient, strategy, fabric8ConfigMapPropertySourceLocator,
k8SConfigurationProperties, threadPoolTaskExecutor);
this.webClient = webClient;
this.kubernetesReactiveDiscoveryClient = k8sReactiveDiscoveryClient;

View File

@@ -25,10 +25,10 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -53,11 +53,11 @@ public class HttpBasedSecretsWatchChangeDetector extends SecretsWatcherChangeDet
public HttpBasedSecretsWatchChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
SecretsPropertySourceLocator secretsPropertySourceLocator,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadPoolTaskExecutor, WebClient webClient,
KubernetesReactiveDiscoveryClient k8sReactiveDiscoveryClient) {
super(environment, properties, kubernetesClient, strategy, secretsPropertySourceLocator,
super(environment, properties, kubernetesClient, strategy, fabric8SecretsPropertySourceLocator,
k8SConfigurationProperties, threadPoolTaskExecutor);
this.webClient = webClient;
this.kubernetesReactiveDiscoveryClient = k8sReactiveDiscoveryClient;

View File

@@ -26,10 +26,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.config.reload.EventBasedSecretsChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.reload.EventBasedSecretsChangeDetector;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -47,10 +47,10 @@ public abstract class SecretsWatcherChangeDetector extends EventBasedSecretsChan
public SecretsWatcherChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
SecretsPropertySourceLocator secretsPropertySourceLocator,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator,
ConfigurationWatcherConfigurationProperties k8SConfigurationProperties,
ThreadPoolTaskExecutor threadPoolTaskExecutor) {
super(environment, properties, kubernetesClient, strategy, secretsPropertySourceLocator);
super(environment, properties, kubernetesClient, strategy, fabric8SecretsPropertySourceLocator);
this.executorService = Executors.newScheduledThreadPool(k8SConfigurationProperties.getThreadPoolSize(),
threadPoolTaskExecutor);
this.k8SConfigurationProperties = k8SConfigurationProperties;

View File

@@ -28,9 +28,9 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -52,7 +52,7 @@ public class BusEventBasedConfigMapWatcherChangeDetectorTests {
private ConfigurationUpdateStrategy updateStrategy;
@Mock
private ConfigMapPropertySourceLocator configMapPropertySourceLocator;
private Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator;
@Mock
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@@ -73,7 +73,7 @@ public class BusEventBasedConfigMapWatcherChangeDetectorTests {
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
busProperties = new BusProperties();
changeDetector = new BusEventBasedConfigMapWatcherChangeDetector(mockEnvironment, configReloadProperties,
client, updateStrategy, configMapPropertySourceLocator, busProperties,
client, updateStrategy, fabric8ConfigMapPropertySourceLocator, busProperties,
configurationWatcherConfigurationProperties, threadPoolTaskExecutor);
changeDetector.setApplicationEventPublisher(applicationEventPublisher);
}

View File

@@ -28,9 +28,9 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.bus.BusProperties;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -52,7 +52,7 @@ public class BusEventBasedSecretsWatcherChangeDetectorTests {
private ConfigurationUpdateStrategy updateStrategy;
@Mock
private SecretsPropertySourceLocator secretsPropertySourceLocator;
private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator;
@Mock
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@@ -73,7 +73,7 @@ public class BusEventBasedSecretsWatcherChangeDetectorTests {
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
busProperties = new BusProperties();
changeDetector = new BusEventBasedSecretsWatcherChangeDetector(mockEnvironment, configReloadProperties, client,
updateStrategy, secretsPropertySourceLocator, busProperties,
updateStrategy, fabric8SecretsPropertySourceLocator, busProperties,
configurationWatcherConfigurationProperties, threadPoolTaskExecutor);
changeDetector.setApplicationEventPublisher(applicationEventPublisher);
}

View File

@@ -38,11 +38,11 @@ import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.reactive.function.client.WebClient;
@@ -74,7 +74,7 @@ public class HttpBasedConfigMapWatchChangeDetectorTests {
private ConfigurationUpdateStrategy updateStrategy;
@Mock
private ConfigMapPropertySourceLocator configMapPropertySourceLocator;
private Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator;
@Mock
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@@ -103,7 +103,7 @@ public class HttpBasedConfigMapWatchChangeDetectorTests {
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
WebClient webClient = WebClient.builder().build();
changeDetector = new HttpBasedConfigMapWatchChangeDetector(mockEnvironment, configReloadProperties, client,
updateStrategy, configMapPropertySourceLocator, configurationWatcherConfigurationProperties,
updateStrategy, fabric8ConfigMapPropertySourceLocator, configurationWatcherConfigurationProperties,
threadPoolTaskExecutor, webClient, reactiveDiscoveryClient);
}

View File

@@ -38,11 +38,11 @@ import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.discovery.KubernetesServiceInstance;
import org.springframework.cloud.kubernetes.discovery.reactive.KubernetesReactiveDiscoveryClient;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.reactive.function.client.WebClient;
@@ -74,7 +74,7 @@ public class HttpBasedSecretsWatchChangeDetectorTests {
private ConfigurationUpdateStrategy updateStrategy;
@Mock
private SecretsPropertySourceLocator secretsPropertySourceLocator;
private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator;
@Mock
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@@ -103,7 +103,7 @@ public class HttpBasedSecretsWatchChangeDetectorTests {
configurationWatcherConfigurationProperties = new ConfigurationWatcherConfigurationProperties();
WebClient webClient = WebClient.builder().build();
changeDetector = new HttpBasedSecretsWatchChangeDetector(mockEnvironment, configReloadProperties, client,
updateStrategy, secretsPropertySourceLocator, configurationWatcherConfigurationProperties,
updateStrategy, fabric8SecretsPropertySourceLocator, configurationWatcherConfigurationProperties,
threadPoolTaskExecutor, webClient, reactiveDiscoveryClient);
}

View File

@@ -77,7 +77,13 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-config</artifactId>
<artifactId>spring-cloud-kubernetes-fabric8-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-client-config</artifactId>
<version>${project.version}</version>
</dependency>

View File

@@ -10,8 +10,8 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-config</artifactId>
<name>Spring Cloud Kubernetes :: Config</name>
<artifactId>spring-cloud-kubernetes-fabric8-config</artifactId>
<name>Spring Cloud Kubernetes :: Fabric8 Config</name>
<dependencies>
<dependency>

View File

@@ -14,17 +14,20 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.Secret;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.kubernetes.commons.KubernetesCommonsAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.KubernetesBootstrapConfiguration;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.cloud.kubernetes.fabric8.Fabric8AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,11 +41,11 @@ import org.springframework.context.annotation.Import;
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.cloud.kubernetes.enabled", matchIfMissing = true)
@ConditionalOnClass({ ConfigMap.class, Secret.class })
public class BootstrapConfiguration {
@AutoConfigureAfter(KubernetesBootstrapConfiguration.class)
public class Fabric8BootstrapConfiguration {
@Configuration(proxyBeanMethods = false)
@Import({ KubernetesCommonsAutoConfiguration.class, Fabric8AutoConfiguration.class })
@EnableConfigurationProperties({ ConfigMapConfigProperties.class, SecretsConfigProperties.class })
protected static class KubernetesPropertySourceConfiguration {
@Autowired
@@ -50,14 +53,15 @@ public class BootstrapConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.config.enabled", matchIfMissing = true)
public ConfigMapPropertySourceLocator configMapPropertySourceLocator(ConfigMapConfigProperties properties) {
return new ConfigMapPropertySourceLocator(this.client, properties);
public Fabric8ConfigMapPropertySourceLocator configMapPropertySourceLocator(
ConfigMapConfigProperties properties) {
return new Fabric8ConfigMapPropertySourceLocator(this.client, properties);
}
@Bean
@ConditionalOnProperty(name = "spring.cloud.kubernetes.secrets.enabled", matchIfMissing = true)
public SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties) {
return new SecretsPropertySourceLocator(this.client, properties);
public Fabric8SecretsPropertySourceLocator secretsPropertySourceLocator(SecretsConfigProperties properties) {
return new Fabric8SecretsPropertySourceLocator(this.client, properties);
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.LinkedHashMap;
import java.util.Map;
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.util.StringUtils;
/**
* A {@link MapPropertySource} that uses Kubernetes config maps.
*
* @author Ioannis Canellos
* @author Ali Shahbour
* @author Michael Moudatsos
*/
public class Fabric8ConfigMapPropertySource extends ConfigMapPropertySource {
private static final Log LOG = LogFactory.getLog(Fabric8ConfigMapPropertySource.class);
public Fabric8ConfigMapPropertySource(KubernetesClient client, String name) {
this(client, name, null, (Environment) null);
}
public Fabric8ConfigMapPropertySource(KubernetesClient client, String name, String namespace,
Environment environment) {
super(getName(name, getNamespace(client, namespace)),
asObjectMap(getData(client, name, getNamespace(client, namespace), environment)));
}
private static String getNamespace(KubernetesClient client, String namespace) {
return StringUtils.isEmpty(namespace) ? client.getNamespace() : namespace;
}
private static Map<String, Object> getData(KubernetesClient client, String name, String namespace,
Environment environment) {
try {
Map<String, Object> result = new LinkedHashMap<>();
ConfigMap map = StringUtils.isEmpty(namespace) ? client.configMaps().withName(name).get()
: client.configMaps().inNamespace(namespace).withName(name).get();
if (map != null) {
result.putAll(processAllEntries(map.getData(), environment));
}
if (environment != null) {
for (String activeProfile : environment.getActiveProfiles()) {
String mapNameWithProfile = name + "-" + activeProfile;
ConfigMap mapWithProfile = StringUtils.isEmpty(namespace)
? client.configMaps().withName(mapNameWithProfile).get()
: client.configMaps().inNamespace(namespace).withName(mapNameWithProfile).get();
if (mapWithProfile != null) {
result.putAll(processAllEntries(mapWithProfile.getData(), environment));
}
}
}
return result;
}
catch (Exception e) {
LOG.warn("Can't read configMap with name: [" + name + "] in namespace:[" + namespace + "]. Ignoring.", e);
}
return new LinkedHashMap<>();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties.NormalizedSource;
import org.springframework.cloud.kubernetes.commons.config.ConfigMapPropertySourceLocator;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import static org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigUtils.getApplicationNamespace;
/**
* A {@link PropertySourceLocator} that uses config maps.
*
* @author Ioannis Canellos
* @author Michael Moudatsos
*/
@Order(0)
public class Fabric8ConfigMapPropertySourceLocator extends ConfigMapPropertySourceLocator {
private final KubernetesClient client;
public Fabric8ConfigMapPropertySourceLocator(KubernetesClient client, ConfigMapConfigProperties properties) {
super(properties);
this.client = client;
}
@Override
protected MapPropertySource getMapPropertySource(String name, NormalizedSource normalizedSource,
String configurationTarget, ConfigurableEnvironment environment) {
return new Fabric8ConfigMapPropertySource(this.client, name,
getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget),
environment);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
/**
* Utility class that works with configuration properties.
*
* @author Ioannis Canellos
*/
public final class Fabric8ConfigUtils {
private static final Log LOG = LogFactory.getLog(Fabric8ConfigUtils.class);
private Fabric8ConfigUtils() {
}
public static String getApplicationNamespace(KubernetesClient client, String configNamespace,
String configurationTarget) {
if (StringUtils.isEmpty(configNamespace)) {
LOG.debug(configurationTarget + " namespace has not been set, taking it from client (ns="
+ client.getNamespace() + ")");
configNamespace = client.getNamespace();
}
return configNamespace;
}
}

View File

@@ -14,9 +14,8 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -25,8 +24,8 @@ import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.util.StringUtils;
/**
@@ -35,20 +34,15 @@ import org.springframework.util.StringUtils;
* @author l burgazzoli
* @author Haytham Mohamed
*/
public class SecretsPropertySource extends MapPropertySource {
public class Fabric8SecretsPropertySource extends SecretsPropertySource {
private static final Log LOG = LogFactory.getLog(SecretsPropertySource.class);
private static final Log LOG = LogFactory.getLog(Fabric8SecretsPropertySource.class);
private static final String PREFIX = "secrets";
public SecretsPropertySource(KubernetesClient client, Environment env, String name, String namespace,
public Fabric8SecretsPropertySource(KubernetesClient client, Environment env, String name, String namespace,
Map<String, String> labels) {
super(getSourceName(client, env, name, namespace), getSourceData(client, env, name, namespace, labels));
}
private static String getSourceName(KubernetesClient client, Environment env, String name, String namespace) {
return new StringBuilder().append(PREFIX).append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(name)
.append(Constants.PROPERTY_SOURCE_NAME_SEPARATOR).append(namespace).toString();
super(getSourceName(name, namespace), getSourceData(client, env, name, namespace, labels));
}
private static Map<String, Object> getSourceData(KubernetesClient client, Environment env, String name,
@@ -85,18 +79,10 @@ public class SecretsPropertySource extends MapPropertySource {
return result;
}
// *****************************
// Helpers
// *****************************
private static void putAll(Secret secret, Map<String, Object> result) {
if (secret != null && secret.getData() != null) {
secret.getData().forEach((k, v) -> result.put(k, new String(Base64.getDecoder().decode(v)).trim()));
if (secret != null) {
putAll(secret.getData(), result);
}
}
@Override
public String toString() {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.fabric8.config;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
import org.springframework.cloud.kubernetes.commons.config.SecretsPropertySourceLocator;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.getApplicationName;
/**
* Kubernetes {@link PropertySourceLocator} for secrets.
*
* @author l burgazzoli
* @author Haytham Mohamed
*/
@Order(1)
public class Fabric8SecretsPropertySourceLocator extends SecretsPropertySourceLocator {
private final KubernetesClient client;
public Fabric8SecretsPropertySourceLocator(KubernetesClient client, SecretsConfigProperties properties) {
super(properties);
this.client = client;
}
@Override
protected MapPropertySource getPropertySource(ConfigurableEnvironment environment,
SecretsConfigProperties.NormalizedSource normalizedSource, String configurationTarget) {
return new Fabric8SecretsPropertySource(this.client, environment,
getApplicationName(environment, normalizedSource.getName(), configurationTarget), Fabric8ConfigUtils
.getApplicationNamespace(this.client, normalizedSource.getNamespace(), configurationTarget),
normalizedSource.getLabels());
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.fabric8.config.reload;
import java.util.concurrent.ThreadLocalRandom;
@@ -33,10 +33,17 @@ import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@@ -79,68 +86,72 @@ public class ConfigReloadAutoConfiguration {
* Polling configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @param fabric8ConfigMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) {
return new PollingConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
return new PollingConfigMapChangeDetector(this.environment, properties, strategy,
Fabric8ConfigMapPropertySource.class, fabric8ConfigMapPropertySourceLocator);
}
/**
* Polling secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @param fabric8SecretsPropertySourceLocator secrets property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) {
return new PollingSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
return new PollingSecretsChangeDetector(this.environment, properties, strategy,
Fabric8SecretsPropertySource.class, fabric8SecretsPropertySourceLocator);
}
/**
* Event Based configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @param fabric8ConfigMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configMap change events and fire a reload.
*/
@Bean
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) {
return new EventBasedConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
fabric8ConfigMapPropertySourceLocator);
}
/**
* Event Based secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @param fabric8SecretsPropertySourceLocator secrets property source locator
* @return a bean that listen to secrets change events and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) {
return new EventBasedSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
fabric8SecretsPropertySourceLocator);
}
/**

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.fabric8.config.reload;
import java.util.concurrent.ThreadLocalRandom;
@@ -25,10 +25,17 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.commons.config.reload.PollingConfigMapChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.PollingSecretsChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.condition.EventReloadDetectionMode;
import org.springframework.cloud.kubernetes.commons.config.reload.condition.PollingReloadDetectionMode;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@@ -62,77 +69,90 @@ public class ConfigReloadDefaultAutoConfiguration {
private KubernetesClient kubernetesClient;
@Autowired
private ConfigMapPropertySourceLocator configMapPropertySourceLocator;
private Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator;
@Autowired
private SecretsPropertySourceLocator secretsPropertySourceLocator;
private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator;
private static void wait(ConfigReloadProperties properties) {
final long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis());
try {
Thread.sleep(waitMillis);
}
catch (InterruptedException ignored) {
}
}
/**
* Polling configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @param fabric8ConfigMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) {
return new PollingConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
return new PollingConfigMapChangeDetector(this.environment, properties, strategy,
Fabric8ConfigMapPropertySource.class, fabric8ConfigMapPropertySourceLocator);
}
/**
* Polling secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @param fabric8SecretsPropertySourceLocator secrets property source locator
* @return a bean that listen to configuration changes and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class)
@Conditional(PollingReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangePollingWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) {
return new PollingSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
return new PollingSecretsChangeDetector(this.environment, properties, strategy,
Fabric8SecretsPropertySource.class, fabric8SecretsPropertySourceLocator);
}
/**
* Event Based configMap ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param configMapPropertySourceLocator configMap property source locator
* @param fabric8ConfigMapPropertySourceLocator configMap property source locator
* @return a bean that listen to configMap change events and fire a reload.
*/
@Bean
@ConditionalOnBean(ConfigMapPropertySourceLocator.class)
@ConditionalOnBean(Fabric8ConfigMapPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector configMapPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) {
return new EventBasedConfigMapChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
configMapPropertySourceLocator);
fabric8ConfigMapPropertySourceLocator);
}
/**
* Event Based secrets ConfigurationChangeDetector.
* @param properties config reload properties
* @param strategy configuration update strategy
* @param secretsPropertySourceLocator secrets property source locator
* @param fabric8SecretsPropertySourceLocator secrets property source locator
* @return a bean that listen to secrets change events and fire a reload.
*/
@Bean
@ConditionalOnBean(SecretsPropertySourceLocator.class)
@ConditionalOnBean(Fabric8SecretsPropertySourceLocator.class)
@Conditional(EventReloadDetectionMode.class)
public ConfigurationChangeDetector secretsPropertyChangeEventWatcher(ConfigReloadProperties properties,
ConfigurationUpdateStrategy strategy, SecretsPropertySourceLocator secretsPropertySourceLocator) {
ConfigurationUpdateStrategy strategy,
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) {
return new EventBasedSecretsChangeDetector(this.environment, properties, this.kubernetesClient, strategy,
secretsPropertySourceLocator);
fabric8SecretsPropertySourceLocator);
}
/**
@@ -154,15 +174,6 @@ public class ConfigReloadDefaultAutoConfiguration {
throw new IllegalStateException("Unsupported configuration update strategy: " + properties.getStrategy());
}
private static void wait(ConfigReloadProperties properties) {
final long waitMillis = ThreadLocalRandom.current().nextLong(properties.getMaxWaitForRestart().toMillis());
try {
Thread.sleep(waitMillis);
}
catch (InterruptedException ignored) {
}
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.fabric8.config.reload;
import java.util.HashMap;
import java.util.Map;
@@ -28,8 +28,11 @@ import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;
/**
@@ -42,19 +45,28 @@ import org.springframework.core.env.AbstractEnvironment;
*/
public class EventBasedConfigMapChangeDetector extends ConfigurationChangeDetector {
private final ConfigMapPropertySourceLocator configMapPropertySourceLocator;
private final Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator;
private final Map<String, Watch> watches;
private KubernetesClient kubernetesClient;
public EventBasedConfigMapChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
ConfigMapPropertySourceLocator configMapPropertySourceLocator) {
super(environment, properties, kubernetesClient, strategy);
this.configMapPropertySourceLocator = configMapPropertySourceLocator;
Fabric8ConfigMapPropertySourceLocator fabric8ConfigMapPropertySourceLocator) {
super(environment, properties, strategy);
this.kubernetesClient = kubernetesClient;
this.fabric8ConfigMapPropertySourceLocator = fabric8ConfigMapPropertySourceLocator;
this.watches = new HashMap<>();
}
@PreDestroy
public void shutdown() {
// Ensure the kubernetes client is cleaned up from spare threads when shutting
// down
this.kubernetesClient.close();
}
@PostConstruct
public void watch() {
boolean activated = false;
@@ -107,8 +119,9 @@ public class EventBasedConfigMapChangeDetector extends ConfigurationChangeDetect
}
protected void onEvent(ConfigMap configMap) {
boolean changed = changed(locateMapPropertySources(this.configMapPropertySourceLocator, this.environment),
findPropertySources(ConfigMapPropertySource.class));
boolean changed = changed(
locateMapPropertySources(this.fabric8ConfigMapPropertySourceLocator, this.environment),
findPropertySources(Fabric8ConfigMapPropertySource.class));
if (changed) {
this.log.info("Detected change in config maps");
reloadProperties();

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.fabric8.config.reload;
import java.util.HashMap;
import java.util.Map;
@@ -28,8 +28,11 @@ import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import org.springframework.cloud.kubernetes.config.SecretsPropertySource;
import org.springframework.cloud.kubernetes.config.SecretsPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8SecretsPropertySourceLocator;
import org.springframework.core.env.AbstractEnvironment;
/**
@@ -42,19 +45,28 @@ import org.springframework.core.env.AbstractEnvironment;
*/
public class EventBasedSecretsChangeDetector extends ConfigurationChangeDetector {
private SecretsPropertySourceLocator secretsPropertySourceLocator;
private Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator;
private Map<String, Watch> watches;
private KubernetesClient kubernetesClient;
public EventBasedSecretsChangeDetector(AbstractEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy,
SecretsPropertySourceLocator secretsPropertySourceLocator) {
super(environment, properties, kubernetesClient, strategy);
this.secretsPropertySourceLocator = secretsPropertySourceLocator;
Fabric8SecretsPropertySourceLocator fabric8SecretsPropertySourceLocator) {
super(environment, properties, strategy);
this.kubernetesClient = kubernetesClient;
this.fabric8SecretsPropertySourceLocator = fabric8SecretsPropertySourceLocator;
this.watches = new HashMap<>();
}
@PreDestroy
public void shutdown() {
// Ensure the kubernetes client is cleaned up from spare threads when shutting
// down
this.kubernetesClient.close();
}
@PostConstruct
public void watch() {
boolean activated = false;
@@ -107,8 +119,8 @@ public class EventBasedSecretsChangeDetector extends ConfigurationChangeDetector
}
protected void onEvent(Secret secret) {
boolean changed = changed(locateMapPropertySources(this.secretsPropertySourceLocator, this.environment),
findPropertySources(SecretsPropertySource.class));
boolean changed = changed(locateMapPropertySources(this.fabric8SecretsPropertySourceLocator, this.environment),
findPropertySources(Fabric8SecretsPropertySource.class));
if (changed) {
this.log.info("Detected change in secrets");
reloadProperties();

View File

@@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.kubernetes.fabric8.config.reload.ConfigReloadAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.kubernetes.fabric8.config.Fabric8BootstrapConfiguration

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.io.IOException;
import java.nio.file.Files;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.io.IOException;
import java.nio.file.Files;
@@ -31,12 +31,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.util.Lists.newArrayList;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.createFileWithContent;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
@@ -86,10 +85,11 @@ public class ConfigMapsFromFilePathsTests {
System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test");
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
Files.createDirectories(Paths.get(FILES_ROOT_PATH + "/" + FILES_SUB_PATH));
createFileWithContent(FIRST_FILE_NAME_FULL_PATH, "bean.greeting=Hello from path!");
createFileWithContent(SECOND_FILE_NAME_FULL_PATH, "bean.farewell=Bye from path!");
createFileWithContent(UNUSED_FILE_NAME_FULL_PATH, "bean.morning=Morning from path!");
createFileWithContent(FIRST_FILE_NAME_DUPLICATED_FULL_PATH, "bean.bonjour=Bonjour from path!");
ConfigMapTestUtil.createFileWithContent(FIRST_FILE_NAME_FULL_PATH, "bean.greeting=Hello from path!");
ConfigMapTestUtil.createFileWithContent(SECOND_FILE_NAME_FULL_PATH, "bean.farewell=Bye from path!");
ConfigMapTestUtil.createFileWithContent(UNUSED_FILE_NAME_FULL_PATH, "bean.morning=Morning from path!");
ConfigMapTestUtil.createFileWithContent(FIRST_FILE_NAME_DUPLICATED_FULL_PATH,
"bean.bonjour=Bonjour from path!");
}
@AfterClass

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.io.IOException;
import java.nio.file.Files;
@@ -33,12 +33,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.util.Lists.newArrayList;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class,
@@ -76,7 +75,8 @@ public class ConfigMapsMixedTests {
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
Files.createDirectories(Paths.get(FILES_ROOT_PATH));
ConfigMapTestUtil.createFileWithContent(FILE_NAME_FULL_PATH, readResourceFile("application-path.yaml"));
ConfigMapTestUtil.createFileWithContent(FILE_NAME_FULL_PATH,
ConfigMapTestUtil.readResourceFile("application-path.yaml"));
HashMap<String, String> data = new HashMap<>();
data.put("bean.morning", "Buenos Dias ConfigMap, %s");

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.Map;
@@ -27,7 +27,7 @@ import org.junit.Rule;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
import static org.springframework.cloud.kubernetes.fabric8.config.ConfigMapTestUtil.readResourceFile;
/**
* @author Charles Moulliard
@@ -74,8 +74,8 @@ public class ConfigMapsTest {
.addToData("application.properties", readResourceFile("application.properties")).build())
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace),
configMapName);
Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource(
this.server.getClient().inNamespace(namespace), configMapName);
assertThat(cmps.getProperty("dummy.property.string1")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int1")).isEqualTo("1");
@@ -91,8 +91,8 @@ public class ConfigMapsTest {
.addToData("application.yaml", readResourceFile("application.yaml")).build())
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace),
configMapName);
Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource(
this.server.getClient().inNamespace(namespace), configMapName);
assertThat(cmps.getProperty("dummy.property.string2")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int2")).isEqualTo(1);
@@ -108,8 +108,8 @@ public class ConfigMapsTest {
.addToData("adhoc.yml", readResourceFile("adhoc.yml")).build())
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace),
configMapName);
Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource(
this.server.getClient().inNamespace(namespace), configMapName);
assertThat(cmps.getProperty("dummy.property.string3")).isEqualTo("a");
assertThat(cmps.getProperty("dummy.property.int3")).isEqualTo(1);
@@ -125,7 +125,7 @@ public class ConfigMapsTest {
.addToData("application.properties", "somevalue").build())
.once();
new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName);
new Fabric8ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName);
// no exception is thrown for unparseable content
}
@@ -139,7 +139,7 @@ public class ConfigMapsTest {
.addToData("application.yaml", "somevalue").build())
.once();
new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName);
new Fabric8ConfigMapPropertySource(this.server.getClient().inNamespace(namespace), configMapName);
// no exception is thrown for unparseable content
}
@@ -155,8 +155,8 @@ public class ConfigMapsTest {
.addToData("adhoc.properties", readResourceFile("adhoc.properties")).build())
.once();
ConfigMapPropertySource cmps = new ConfigMapPropertySource(this.server.getClient().inNamespace(namespace),
configMapName);
Fabric8ConfigMapPropertySource cmps = new Fabric8ConfigMapPropertySource(
this.server.getClient().inNamespace(namespace), configMapName);
// application.properties should be read correctly
assertThat(cmps.getProperty("dummy.property.string1")).isEqualTo("a");

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
@@ -30,13 +30,13 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
import static org.springframework.cloud.kubernetes.fabric8.config.ConfigMapTestUtil.readResourceFile;
/**
* @author Ali Shahbour

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
@@ -30,13 +30,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
/**
* Tests reading property from YAML document specified by profile expression.
*/
@@ -68,7 +66,7 @@ public class ConfigMapsWithProfileExpressionTests {
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
HashMap<String, String> data = new HashMap<>();
data.put("application.yml", readResourceFile("application-with-profiles.yaml"));
data.put("application.yml", ConfigMapTestUtil.readResourceFile("application-with-profiles.yaml"));
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME)
.andReturn(200, new ConfigMapBuilder().withNewMetadata().withName(APPLICATION_NAME).endMetadata()
.addToData(data).build())

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
@@ -30,11 +30,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
import static org.springframework.cloud.kubernetes.fabric8.config.ConfigMapTestUtil.readResourceFile;
/**
* @author Charles Moulliard

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
@@ -30,13 +30,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
/**
* @author Charles Moulliard
*/
@@ -73,7 +71,7 @@ public class ConfigMapsWithProfilesTests {
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
HashMap<String, String> data = new HashMap<>();
data.put("application.yml", readResourceFile("application-with-profiles.yaml"));
data.put("application.yml", ConfigMapTestUtil.readResourceFile("application-with-profiles.yaml"));
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME)
.andReturn(200, new ConfigMapBuilder().withNewMetadata().withName(APPLICATION_NAME).endMetadata()
.addToData(data).build())

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
@@ -30,13 +30,11 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.cloud.kubernetes.config.ConfigMapTestUtil.readResourceFile;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class, properties = {
"spring.application.name=configmap-without-profile-example", "spring.cloud.kubernetes.reload.enabled=false" })
@@ -67,7 +65,7 @@ public class ConfigMapsWithoutProfilesTests {
System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true");
HashMap<String, String> data = new HashMap<>();
data.put("application.yml", readResourceFile("application-without-profiles.yaml"));
data.put("application.yml", ConfigMapTestUtil.readResourceFile("application-without-profiles.yaml"));
server.expect().withPath("/api/v1/namespaces/test/configmaps/" + APPLICATION_NAME)
.andReturn(200, new ConfigMapBuilder().withNewMetadata().withName(APPLICATION_NAME).endMetadata()
.addToData(data).build())

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.Base64;
@@ -30,7 +30,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
@@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = App.class)
@TestPropertySource("classpath:/application-secrets.properties")
public class SecretsPropertySourceTest {
public class Fabric8SecretsPropertySourceTest {
private static final String NAMESPACE = "test";
@@ -51,7 +51,7 @@ public class SecretsPropertySourceTest {
public static KubernetesServer server = new KubernetesServer(false, true);
@Autowired
private SecretsPropertySourceLocator propertySourceLocator;
private Fabric8SecretsPropertySourceLocator propertySourceLocator;
@Autowired
private Environment environment;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -27,7 +27,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example.App;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.After;
@@ -23,7 +23,7 @@ import org.junit.Test;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.kubernetes.config.reload.ConfigReloadAutoConfiguration;
import org.springframework.cloud.kubernetes.fabric8.config.reload.ConfigReloadAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -124,7 +124,7 @@ public class KubernetesConfigConfigurationTest {
private void setup(String... env) {
this.context = new SpringApplicationBuilder(PropertyPlaceholderAutoConfiguration.class,
KubernetesClientTestConfiguration.class, BootstrapConfiguration.class,
KubernetesClientTestConfiguration.class, Fabric8BootstrapConfiguration.class,
ConfigReloadAutoConfiguration.class, RefreshAutoConfiguration.class)
.web(org.springframework.boot.WebApplicationType.NONE).properties(env).run();
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import org.junit.Rule;
import org.junit.Test;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.HashMap;
import java.util.Map;
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example2.ExampleApp;
import org.springframework.cloud.kubernetes.fabric8.config.example2.ExampleApp;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import java.util.Base64;
import java.util.HashMap;
@@ -33,7 +33,7 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.kubernetes.config.example3.MultiSecretsApp;
import org.springframework.cloud.kubernetes.fabric8.config.example3.MultiSecretsApp;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config;
package org.springframework.cloud.kubernetes.fabric8.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example;
package org.springframework.cloud.kubernetes.fabric8.config.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example;
package org.springframework.cloud.kubernetes.fabric8.config.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example;
package org.springframework.cloud.kubernetes.fabric8.config.example;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example;
package org.springframework.cloud.kubernetes.fabric8.config.example;
/**
* @author Charles Moulliard

View File

@@ -14,11 +14,12 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example2;
package org.springframework.cloud.kubernetes.fabric8.config.example2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.kubernetes.fabric8.config.example.App;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -27,7 +28,7 @@ import org.springframework.web.bind.annotation.RestController;
public class ExampleApp {
public static void main(String[] args) {
SpringApplication.run(org.springframework.cloud.kubernetes.config.example.App.class, args);
SpringApplication.run(App.class, args);
}
@RestController

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example2;
package org.springframework.cloud.kubernetes.fabric8.config.example2;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example3;
package org.springframework.cloud.kubernetes.fabric8.config.example3;
/**
* @author Haytham Mohamed

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.example3;
package org.springframework.cloud.kubernetes.fabric8.config.example3;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@@ -14,17 +14,19 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.fabric8.config.reload;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationChangeDetector;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
@@ -33,7 +35,7 @@ import org.springframework.core.env.MapPropertySource;
*/
public class ConfigurationChangeDetectorTest {
private final ConfigurationChangeDetectorStub stub = new ConfigurationChangeDetectorStub(null, null, null, null);
private final ConfigurationChangeDetectorStub stub = new ConfigurationChangeDetectorStub(null, null, null);
@Test
public void testChangedTwoNulls() {
@@ -122,8 +124,8 @@ public class ConfigurationChangeDetectorTest {
private static final class ConfigurationChangeDetectorStub extends ConfigurationChangeDetector {
private ConfigurationChangeDetectorStub(ConfigurableEnvironment environment, ConfigReloadProperties properties,
KubernetesClient kubernetesClient, ConfigurationUpdateStrategy strategy) {
super(environment, properties, kubernetesClient, strategy);
ConfigurationUpdateStrategy strategy) {
super(environment, properties, strategy);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.kubernetes.config.reload;
package org.springframework.cloud.kubernetes.fabric8.config.reload;
import java.util.HashMap;
import java.util.List;
@@ -29,8 +29,10 @@ import io.fabric8.kubernetes.client.dsl.Resource;
import org.junit.Test;
import org.springframework.cloud.bootstrap.config.BootstrapPropertySource;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.config.ConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigReloadProperties;
import org.springframework.cloud.kubernetes.commons.config.reload.ConfigurationUpdateStrategy;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySource;
import org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigMapPropertySourceLocator;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
@@ -59,14 +61,16 @@ public class EventBasedConfigurationChangeDetectorTests {
when(mixedOperation.withName(eq("myconfigmap"))).thenReturn(resource);
when(k8sClient.configMaps()).thenReturn(mixedOperation);
ConfigMapPropertySource configMapPropertySource = new ConfigMapPropertySource(k8sClient, "myconfigmap");
env.getPropertySources().addFirst(new BootstrapPropertySource(configMapPropertySource));
Fabric8ConfigMapPropertySource fabric8ConfigMapPropertySource = new Fabric8ConfigMapPropertySource(k8sClient,
"myconfigmap");
env.getPropertySources().addFirst(new BootstrapPropertySource(fabric8ConfigMapPropertySource));
ConfigurationUpdateStrategy configurationUpdateStrategy = mock(ConfigurationUpdateStrategy.class);
ConfigMapPropertySourceLocator configMapLocator = mock(ConfigMapPropertySourceLocator.class);
Fabric8ConfigMapPropertySourceLocator configMapLocator = mock(Fabric8ConfigMapPropertySourceLocator.class);
EventBasedConfigMapChangeDetector detector = new EventBasedConfigMapChangeDetector(env, configReloadProperties,
k8sClient, configurationUpdateStrategy, configMapLocator);
List<ConfigMapPropertySource> sources = detector.findPropertySources(ConfigMapPropertySource.class);
List<Fabric8ConfigMapPropertySource> sources = detector
.findPropertySources(Fabric8ConfigMapPropertySource.class);
assertThat(sources.size()).isEqualTo(1);
assertThat(sources.get(0).getProperty("foo")).isEqualTo("bar");
}

Some files were not shown because too many files have changed in this diff Show More