Signed-off-by: wind57 <eugen.rabii@gmail.com>
This commit is contained in:
wind57
2025-03-25 19:58:44 +02:00
parent 7c441dfb2b
commit 1e70cab4e3
12 changed files with 39 additions and 38 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -41,7 +41,7 @@ public class Controller {
@GetMapping("/secret")
public String secret() {
return configMapProperties.getKey();
return secretsProperties.getKey();
}
}

View File

@@ -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;

View File

@@ -12,6 +12,4 @@ spring:
config:
paths:
- /tmp/application.properties
config:
import: "kubernetes:"

View File

@@ -1,6 +1,6 @@
spring:
cloud:
kubernetes:
config:
secrets:
paths:
- /tmp/application.properties

View File

@@ -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);
});
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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"

View File

@@ -44,4 +44,4 @@ spec:
- name: "config-map-volume"
configMap:
defaultMode: 420
name: "poll-reload"
name: "configmap-reload"

View File

@@ -4,6 +4,6 @@ metadata:
name: secret-reload
namespace: default
data:
# from.properties.key=initial
# from.properties.secret.key=initial
application.properties: |
ZnJvbS5wcm9wZXJ0aWVzLmtleT1pbml0aWFs
ZnJvbS5wcm9wZXJ0aWVzLnNlY3JldC5rZXk9aW5pdGlhbA==