From 1e70cab4e364ede0b82624ab6423d75a2b79150f Mon Sep 17 00:00:00 2001 From: wind57 Date: Tue, 25 Mar 2025 19:58:44 +0200 Subject: [PATCH] dirty Signed-off-by: wind57 --- .../config/SecretsPropertySourceLocator.java | 12 ++++++---- .../client/reload/ConfigMapProperties.java | 2 +- .../k8s/client/reload/Controller.java | 2 +- .../k8s/client/reload/SecretsProperties.java | 2 +- .../src/main/resources/application-mount.yaml | 2 -- .../resources/bootstrap-with-bootstrap.yaml | 2 +- .../it/K8sClientConfigMapMountPollingIT.java | 14 +++++------ ...8sClientSecretMountBootstrapPollingIT.java | 24 ++++++++----------- .../src/test/resources/mount/configmap.yaml | 4 ++-- .../mount/deployment-with-secret.yaml | 7 ++++-- .../src/test/resources/mount/deployment.yaml | 2 +- .../src/test/resources/mount/secret.yaml | 4 ++-- 12 files changed, 39 insertions(+), 38 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java index 07365b3f..87692ba6 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsPropertySourceLocator.java @@ -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 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); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/ConfigMapProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/ConfigMapProperties.java index f6e65df3..bc97eed9 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/ConfigMapProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/ConfigMapProperties.java @@ -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; diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/Controller.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/Controller.java index 660ed1dc..ffee7d53 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/Controller.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/Controller.java @@ -41,7 +41,7 @@ public class Controller { @GetMapping("/secret") public String secret() { - return configMapProperties.getKey(); + return secretsProperties.getKey(); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/SecretsProperties.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/SecretsProperties.java index 43615d87..bcea2864 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/SecretsProperties.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/java/org/springframework/cloud/kubernetes/k8s/client/reload/SecretsProperties.java @@ -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; diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/application-mount.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/application-mount.yaml index 62548724..ed67e042 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/application-mount.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/application-mount.yaml @@ -12,6 +12,4 @@ spring: config: paths: - /tmp/application.properties - config: - import: "kubernetes:" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/bootstrap-with-bootstrap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/bootstrap-with-bootstrap.yaml index 0aed2eb9..8fcaa31b 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/bootstrap-with-bootstrap.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/main/resources/bootstrap-with-bootstrap.yaml @@ -1,6 +1,6 @@ spring: cloud: kubernetes: - config: + secrets: paths: - /tmp/application.properties diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientConfigMapMountPollingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientConfigMapMountPollingIT.java index 8a9d9bc0..8337028f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientConfigMapMountPollingIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientConfigMapMountPollingIT.java @@ -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); + }); } } diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java index 87568eb0..aaaafe53 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/java/org/springframework/cloud/kubernetes/k8s/client/reload/it/K8sClientSecretMountBootstrapPollingIT.java @@ -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); diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/configmap.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/configmap.yaml index dd14c314..d6b8885f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/configmap.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/configmap.yaml @@ -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 diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment-with-secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment-with-secret.yaml index 1ac93cf9..9110c1f5 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment-with-secret.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment-with-secret.yaml @@ -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" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment.yaml index 22184fa4..9261862f 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/deployment.yaml @@ -44,4 +44,4 @@ spec: - name: "config-map-volume" configMap: defaultMode: 420 - name: "poll-reload" + name: "configmap-reload" diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/secret.yaml b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/secret.yaml index 26c9c3e2..ffcd14ee 100644 --- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/secret.yaml +++ b/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-k8s-client-reload/src/test/resources/mount/secret.yaml @@ -4,6 +4,6 @@ metadata: name: secret-reload namespace: default data: - # from.properties.key=initial + # from.properties.secret.key=initial application.properties: | - ZnJvbS5wcm9wZXJ0aWVzLmtleT1pbml0aWFs + ZnJvbS5wcm9wZXJ0aWVzLnNlY3JldC5rZXk9aW5pdGlhbA==