@@ -25,6 +25,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
@@ -123,12 +124,16 @@ public abstract class SecretsPropertySourceLocator implements PropertySourceLoca
|
||||
|
||||
protected void putPathConfig(CompositePropertySource composite) {
|
||||
|
||||
if (!properties.paths().isEmpty()) {
|
||||
Set<String> uniquePaths = new LinkedHashSet<>(properties.paths());
|
||||
|
||||
if (!uniquePaths.isEmpty()) {
|
||||
LOG.warn(
|
||||
"path support is deprecated and will be removed in a future release. Please use spring.config.import");
|
||||
}
|
||||
|
||||
this.properties.paths().stream().map(Paths::get).filter(Files::exists).flatMap(x -> {
|
||||
LOG.debug("paths property sources : " + uniquePaths);
|
||||
|
||||
uniquePaths.stream().map(Paths::get).filter(Files::exists).flatMap(x -> {
|
||||
try {
|
||||
return Files.walk(x);
|
||||
}
|
||||
@@ -136,8 +141,7 @@ public abstract class SecretsPropertySourceLocator implements PropertySourceLoca
|
||||
LOG.warn("Error walking properties files", e);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
}).filter(Objects::nonNull)
|
||||
.filter(Files::isRegularFile)
|
||||
.collect(new SecretsPropertySourceCollector())
|
||||
.forEach(composite::addPropertySource);
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@ConfigurationProperties("from.properties")
|
||||
@ConfigurationProperties("from.properties.configmap")
|
||||
public class ConfigMapProperties {
|
||||
|
||||
private String key;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class Controller {
|
||||
|
||||
@GetMapping("/secret")
|
||||
public String secret() {
|
||||
return configMapProperties.getKey();
|
||||
return secretsProperties.getKey();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
/**
|
||||
* @author wind57
|
||||
*/
|
||||
@ConfigurationProperties("from.properties")
|
||||
@ConfigurationProperties("from.properties.secret")
|
||||
public class SecretsProperties {
|
||||
|
||||
private String key;
|
||||
|
||||
@@ -12,6 +12,4 @@ spring:
|
||||
config:
|
||||
paths:
|
||||
- /tmp/application.properties
|
||||
config:
|
||||
import: "kubernetes:"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
spring:
|
||||
cloud:
|
||||
kubernetes:
|
||||
config:
|
||||
secrets:
|
||||
paths:
|
||||
- /tmp/application.properties
|
||||
|
||||
@@ -105,20 +105,20 @@ class K8sClientConfigMapMountPollingIT extends K8sClientReloadBase {
|
||||
// replace data in configmap and wait for k8s to pick it up
|
||||
// our polling will detect that and restart the app
|
||||
V1ConfigMap configMap = (V1ConfigMap) util.yaml("mount/configmap.yaml");
|
||||
configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed"));
|
||||
coreV1Api.replaceNamespacedConfigMap("poll-reload", NAMESPACE, configMap, null, null, null, null);
|
||||
configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.configmap.key=as-mount-changed"));
|
||||
coreV1Api.replaceNamespacedConfigMap("configmap-reload", NAMESPACE, configMap, null, null, null, null);
|
||||
|
||||
Commons.waitForLogStatement("Detected change in config maps/secrets, reload will be triggered", K3S,
|
||||
IMAGE_NAME);
|
||||
|
||||
await().atMost(Duration.ofSeconds(120))
|
||||
.pollInterval(Duration.ofSeconds(1))
|
||||
.until(() -> webClient.method(HttpMethod.GET)
|
||||
await().atMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(1)).until(() -> {
|
||||
String local = webClient.method(HttpMethod.GET)
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.retryWhen(retrySpec())
|
||||
.block()
|
||||
.equals("as-mount-changed"));
|
||||
.block();
|
||||
return "as-mount-changed".equals(local);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.k8s.client.reload.it;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
import io.kubernetes.client.openapi.apis.CoreV1Api;
|
||||
import io.kubernetes.client.openapi.models.V1ConfigMap;
|
||||
import io.kubernetes.client.openapi.models.V1Secret;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -78,7 +80,7 @@ class K8sClientSecretMountBootstrapPollingIT extends K8sClientReloadBase {
|
||||
* is 'spring.cloud.kubernetes.secret.paths', which we have set to
|
||||
* '/tmp/application.properties'
|
||||
* in this test. That is populated by the volumeMounts (see mount/deployment-with-secret.yaml)
|
||||
* - we first assert that we are actually reading the path based source via (1), (2) and (3).
|
||||
* - we first assert that we are actually reading the path based source
|
||||
*
|
||||
* - we then change the secret content, wait for k8s to pick it up and replace them
|
||||
* - our polling will then detect that change, and trigger a reload.
|
||||
@@ -86,12 +88,6 @@ class K8sClientSecretMountBootstrapPollingIT extends K8sClientReloadBase {
|
||||
*/
|
||||
@Test
|
||||
void test() throws Exception {
|
||||
// (1)
|
||||
Commons.waitForLogStatement("paths property sources : [/tmp/application.properties]", K3S, IMAGE_NAME);
|
||||
// (2)
|
||||
Commons.waitForLogStatement("will add file-based property source : /tmp/application.properties", K3S,
|
||||
IMAGE_NAME);
|
||||
// (3)
|
||||
WebClient webClient = builder().baseUrl("http://localhost:32321/secret").build();
|
||||
String result = webClient.method(HttpMethod.GET)
|
||||
.retrieve()
|
||||
@@ -100,15 +96,15 @@ class K8sClientSecretMountBootstrapPollingIT extends K8sClientReloadBase {
|
||||
.block();
|
||||
|
||||
// we first read the initial value from the configmap
|
||||
assertThat(result).isEqualTo("as-mount-initial");
|
||||
assertThat(result).isEqualTo("initial");
|
||||
|
||||
// replace data in configmap and wait for k8s to pick it up
|
||||
// replace data in secret and wait for k8s to pick it up
|
||||
// our polling will detect that and restart the app
|
||||
V1ConfigMap configMap = (V1ConfigMap) util.yaml("mount/secret.yaml");
|
||||
configMap.setData(Map.of(Constants.APPLICATION_PROPERTIES, "from.properties.key=as-mount-changed"));
|
||||
coreV1Api.replaceNamespacedConfigMap("poll-reload", NAMESPACE, configMap, null, null, null, null);
|
||||
V1Secret secret = (V1Secret) util.yaml("mount/secret.yaml");
|
||||
secret.setData(Map.of(Constants.APPLICATION_PROPERTIES, Base64.getEncoder()
|
||||
.encode("from.properties.secret.key=as-mount-changed".getBytes(StandardCharsets.UTF_8))));
|
||||
coreV1Api.readNamespacedSecret("secret-reload", NAMESPACE, null);
|
||||
|
||||
System.out.println("Waiting for reload change to be observed");
|
||||
Commons.waitForLogStatement("Detected change in config maps/secrets, reload will be triggered", K3S,
|
||||
IMAGE_NAME);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: poll-reload
|
||||
name: configmap-reload # different from the application name
|
||||
namespace: default
|
||||
data:
|
||||
application.properties: |
|
||||
from.properties.key=as-mount-initial
|
||||
from.properties.configmap.key=as-mount-initial
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
serviceAccountName: spring-cloud-kubernetes-serviceaccount
|
||||
containers:
|
||||
- name: spring-cloud-kubernetes-k8s-client-reload
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-k8s-client-reload
|
||||
image: docker.io/springcloud/spring-cloud-kubernetes-k8s-client-reload:1
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
@@ -32,11 +32,14 @@ spec:
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: "with-bootstrap"
|
||||
- name: SPRING_CLOUD_BOOTSTRAP_ENABLED
|
||||
value: TRUE
|
||||
value: "TRUE"
|
||||
- name: LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_KUBERNETES_COMMONS
|
||||
value: DEBUG
|
||||
|
||||
volumeMounts:
|
||||
- mountPath: /tmp
|
||||
name: "secret-volume"
|
||||
readOnly: true
|
||||
|
||||
volumes:
|
||||
- name: "secret-volume"
|
||||
|
||||
@@ -44,4 +44,4 @@ spec:
|
||||
- name: "config-map-volume"
|
||||
configMap:
|
||||
defaultMode: 420
|
||||
name: "poll-reload"
|
||||
name: "configmap-reload"
|
||||
|
||||
@@ -4,6 +4,6 @@ metadata:
|
||||
name: secret-reload
|
||||
namespace: default
|
||||
data:
|
||||
# from.properties.key=initial
|
||||
# from.properties.secret.key=initial
|
||||
application.properties: |
|
||||
ZnJvbS5wcm9wZXJ0aWVzLmtleT1pbml0aWFs
|
||||
ZnJvbS5wcm9wZXJ0aWVzLnNlY3JldC5rZXk9aW5pdGlhbA==
|
||||
|
||||
Reference in New Issue
Block a user