result = CACHE.computeIfAbsent(namespace, x -> {
try {
b[0] = true;
- return strippedSecrets(coreV1Api
- .listNamespacedSecret(namespace, null, null, null, null, null, null, null, null, null, null)
- .getItems());
+ return strippedSecrets(coreV1Api.listNamespacedSecret(namespace, null, null, null, null, null, null,
+ null, null, null, null, null).getItems());
}
catch (ApiException apiException) {
throw new RuntimeException(apiException.getResponseBody(), apiException);
diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java
index f5f36591ea..0581017f69 100644
--- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java
+++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedConfigMapChangeDetector.java
@@ -123,11 +123,12 @@ void inform() {
}
SharedInformerFactory factory = new SharedInformerFactory(apiClient);
factories.add(factory);
- informer = factory.sharedIndexInformerFor(
- (CallGeneratorParams params) -> coreV1Api.listNamespacedConfigMapCall(namespace, null, null, null,
- null, filter[0], null, params.resourceVersion, null, params.timeoutSeconds, params.watch,
- null),
- V1ConfigMap.class, V1ConfigMapList.class);
+ informer = factory
+ .sharedIndexInformerFor(
+ (CallGeneratorParams params) -> coreV1Api.listNamespacedConfigMapCall(namespace, null, null,
+ null, null, filter[0], null, params.resourceVersion, null, null,
+ params.timeoutSeconds, params.watch, null),
+ V1ConfigMap.class, V1ConfigMapList.class);
LOG.debug(() -> "added configmap informer for namespace : " + namespace + " with filter : " + filter[0]);
diff --git a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java
index 2beb30dbdc..f97da12a2b 100644
--- a/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java
+++ b/spring-cloud-kubernetes-client-config/src/main/java/org/springframework/cloud/kubernetes/client/config/reload/KubernetesClientEventBasedSecretsChangeDetector.java
@@ -128,8 +128,8 @@ void inform() {
factories.add(factory);
informer = factory.sharedIndexInformerFor(
(CallGeneratorParams params) -> coreV1Api.listNamespacedSecretCall(namespace, null, null, null,
- null, filter[0], null, params.resourceVersion, null, params.timeoutSeconds, params.watch,
- null),
+ null, filter[0], null, params.resourceVersion, null, null, params.timeoutSeconds,
+ params.watch, null),
V1Secret.class, V1SecretList.class);
LOG.debug(() -> "added secret informer for namespace : " + namespace + " with filter : " + filter[0]);
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolverTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolverTests.java
index 3e234007d1..8442cf5591 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolverTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/KubernetesClientConfigDataLocationResolverTests.java
@@ -22,6 +22,7 @@
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.boot.DefaultBootstrapContext;
@@ -31,6 +32,8 @@
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.logging.DeferredLogFactory;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.config.ConfigDataRetryableConfigMapPropertySourceLocator;
import org.springframework.cloud.kubernetes.commons.config.ConfigDataRetryableSecretsPropertySourceLocator;
@@ -43,6 +46,7 @@
/**
* @author wind57
*/
+@ExtendWith(OutputCaptureExtension.class)
class KubernetesClientConfigDataLocationResolverTests {
private static final DeferredLogFactory FACTORY = Supplier::get;
@@ -142,7 +146,7 @@ void testBothPresent() {
* these are not retryable beans.
*/
@Test
- void testBothPresentExplicitly() {
+ void testBothPresentExplicitly(CapturedOutput capturedOutput) {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("spring.cloud.kubernetes.config.enabled", "true");
environment.setProperty("spring.cloud.kubernetes.secrets.enabled", "true");
@@ -172,6 +176,10 @@ void testBothPresentExplicitly() {
SecretsPropertySourceLocator secretsPropertySourceLocator = context.get(SecretsPropertySourceLocator.class);
Assertions.assertSame(KubernetesClientSecretsPropertySourceLocator.class,
secretsPropertySourceLocator.getClass());
+
+ Assertions.assertTrue(capturedOutput.getOut()
+ .contains("Could not create the Kubernetes ApiClient in a cluster environment, because connection port "
+ + "was not provided."));
}
/*
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixConfigDataTests.java
index c4e0fe404b..3af64c5210 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixConfigDataTests.java
@@ -29,7 +29,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithPrefixConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledConfigMapWithPrefixConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixTests.java
index 647e3cb6c3..b2bd7da8ad 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_prefix/LabeledConfigMapWithPrefixTests.java
@@ -26,8 +26,8 @@
import org.springframework.test.web.reactive.server.WebTestClient;
/**
- * Stud data is in
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithPrefixConfigurationStub}
+ * Stub data is in
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledConfigMapWithPrefixConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileConfigDataTests.java
index 4e3991b126..df53725772 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileConfigDataTests.java
@@ -30,7 +30,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithProfileConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledConfigMapWithProfileConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java
index 4a206dab65..a382d020f0 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_config_map_with_profile/LabeledConfigMapWithProfileTests.java
@@ -26,8 +26,8 @@
import org.springframework.test.web.reactive.server.WebTestClient;
/**
- * Stud data is in
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithProfileConfigurationStub}
+ * Stub data is in
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledConfigMapWithProfileConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixConfigDataTests.java
index 42cbe38aa6..ce0c8dc358 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixConfigDataTests.java
@@ -29,7 +29,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithPrefixConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledSecretWithPrefixConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixTests.java
index 6300f24f2d..8577c1d72f 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_prefix/LabeledSecretWithPrefixTests.java
@@ -23,7 +23,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithPrefixConfigurationStub;
+import org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledSecretWithPrefixConfigurationStub;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileConfigDataTests.java
index 14cff5d216..50e17abb45 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileConfigDataTests.java
@@ -30,7 +30,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithProfileConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledSecretWithProfileConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java
index fbcb546a78..9244434bbb 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/labeled_secret_with_profile/LabeledSecretWithProfileTests.java
@@ -39,7 +39,7 @@
/**
* Stubs for this test are in
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithProfileConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledSecretWithProfileConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixConfigDataTests.java
index 4bd5cdbd85..62ea60ef8b 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixConfigDataTests.java
@@ -29,7 +29,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithPrefixConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithPrefixConfigurationStub.stubData;
/**
* @author Ryan Baxter
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixTests.java
index 881b628416..71d4359ea1 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_prefix/NamedConfigMapWithPrefixTests.java
@@ -27,7 +27,7 @@
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithPrefixConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithPrefixConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileApp.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileApp.java
index 81db4b8077..5ce6f69d11 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileApp.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileApp.java
@@ -25,7 +25,7 @@
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithProfileConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithProfileConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileConfigDataTests.java
index d3c660d3d0..449aa6a28e 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileConfigDataTests.java
@@ -30,7 +30,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithProfileConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithProfileConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileTests.java
index a997b4e037..21a86346ac 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_config_map_with_profile/NamedConfigMapWithProfileTests.java
@@ -27,7 +27,7 @@
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithProfileConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithProfileConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixConfigDataTests.java
index bacf92cb44..de518cf1ed 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixConfigDataTests.java
@@ -29,7 +29,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithPrefixConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedSecretWithPrefixConfigurationStub.stubData;
/**
* @author Ryan Baxter
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixTests.java
index 58a6545d2a..68c0de45c1 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_prefix/NamedSecretWithPrefixTests.java
@@ -27,7 +27,7 @@
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithPrefixConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedSecretWithPrefixConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileConfigDataTests.java
index 327af36374..3b036954f2 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileConfigDataTests.java
@@ -30,7 +30,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithProfileConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedSecretWithProfileConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileTests.java
index 20093f5dc5..631a2db87a 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/named_secret_with_profile/NamedSecretWithProfileTests.java
@@ -27,7 +27,7 @@
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithProfileConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedSecretWithProfileConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java
index fa56b6811b..64fa102f89 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesConfigDataTests.java
@@ -30,7 +30,7 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SingleSourceMultipleFilesConfigurationStub.stubData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SingleSourceMultipleFilesConfigurationStub.stubData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java
index 6ba97caee4..4dd41e9ab0 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/single_source_multiple_files/SingleSourceMultipleFilesTests.java
@@ -29,7 +29,7 @@
* @author wind57
*
* Stub for this test is here :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SingleSourceMultipleFilesConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SingleSourceMultipleFilesConfigurationStub}
*
* issue: https://github.com/spring-cloud/spring-cloud-kubernetes/issues/640
*
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataRetryableSourcesOrderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataRetryableSourcesOrderTests.java
index f8c23716bb..59e075dd89 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataRetryableSourcesOrderTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataRetryableSourcesOrderTests.java
@@ -32,12 +32,12 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub.stubConfigMapData;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub.stubSecretsData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub.stubConfigMapData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub.stubSecretsData;
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataSourcesOrderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataSourcesOrderTests.java
index 1aea3d0290..7b32c3ef98 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataSourcesOrderTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/ConfigDataSourcesOrderTests.java
@@ -29,8 +29,8 @@
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.mockito.Mockito.mockStatic;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub.stubConfigMapData;
-import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub.stubSecretsData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub.stubConfigMapData;
+import static org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub.stubSecretsData;
/**
* @author wind57
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java
index 7e7d59f2ba..a32ede6fa0 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/applications/sources_order/SourcesOrderTests.java
@@ -31,7 +31,7 @@
/**
* The stub data for this test is in :
- * {@link org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub}
+ * {@link org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub}
*
* @author wind57
*/
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/BootstrapKubernetesClientSanitizeEnvEndpointStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/BootstrapKubernetesClientSanitizeEnvEndpointStub.java
new file mode 100644
index 0000000000..872120e04b
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/BootstrapKubernetesClientSanitizeEnvEndpointStub.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2013-2023 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.boostrap.stubs;
+
+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.JSON;
+import io.kubernetes.client.openapi.models.V1ConfigMap;
+import io.kubernetes.client.openapi.models.V1ConfigMapBuilder;
+import io.kubernetes.client.openapi.models.V1ConfigMapList;
+import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
+import io.kubernetes.client.openapi.models.V1Secret;
+import io.kubernetes.client.openapi.models.V1SecretBuilder;
+import io.kubernetes.client.openapi.models.V1SecretList;
+import io.kubernetes.client.util.ClientBuilder;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+
+/**
+ * A test bootstrap that takes care to initialize ApiClient _before_ our main bootstrap
+ * context; with some stub data already present.
+ *
+ * @author wind57
+ */
+@Order(0)
+@Configuration
+@ConditionalOnProperty("bootstrap.sanitize")
+public class BootstrapKubernetesClientSanitizeEnvEndpointStub {
+
+ @Bean
+ public WireMockServer wireMock() {
+ WireMockServer server = new WireMockServer(options().dynamicPort());
+ server.start();
+ WireMock.configureFor("localhost", server.port());
+ return server;
+ }
+
+ @Bean
+ public ApiClient apiClient(WireMockServer wireMockServer) {
+ ApiClient apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build();
+ io.kubernetes.client.openapi.Configuration.setDefaultApiClient(apiClient);
+ apiClient.setDebugging(true);
+ stubData();
+ return apiClient;
+ }
+
+ public static void stubData() {
+
+ V1ConfigMap one = new V1ConfigMapBuilder()
+ .withMetadata(new V1ObjectMetaBuilder().withName("sanitize-configmap").withNamespace("test").build())
+ .addToData(Map.of("sanitize.sanitizeConfigMapName", "sanitizeConfigMapValue")).build();
+
+ V1Secret secretOne = new V1SecretBuilder()
+ .withMetadata(new V1ObjectMetaBuilder().withName("sanitize-secret").withNamespace("test").build())
+ .addToData(Map.of("sanitize.sanitizeSecretName", "sanitizeSecretValue".getBytes())).build();
+
+ V1Secret secretTwo = new V1SecretBuilder()
+ .withMetadata(new V1ObjectMetaBuilder().withName("sanitize-secret-two").withNamespace("test").build())
+ .addToData(Map.of("sanitize.sanitizeSecretNameTwo", "sanitizeSecretValueTwo".getBytes())).build();
+
+ // the actual stub for CoreV1Api calls
+ V1ConfigMapList configMapList = new V1ConfigMapList();
+ configMapList.addItemsItem(one);
+
+ V1SecretList secretList = new V1SecretList();
+ secretList.addItemsItem(secretOne);
+ secretList.addItemsItem(secretTwo);
+
+ WireMock.stubFor(WireMock.get("/api/v1/namespaces/test/configmaps")
+ .willReturn(WireMock.aResponse().withStatus(200).withBody(new JSON().serialize(configMapList))));
+
+ WireMock.stubFor(WireMock.get("/api/v1/namespaces/test/secrets")
+ .willReturn(WireMock.aResponse().withStatus(200).withBody(new JSON().serialize(secretList))));
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledConfigMapWithPrefixConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithPrefixConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledConfigMapWithPrefixConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithPrefixConfigurationStub.java
index c55e19164a..cce9bdd9c1 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledConfigMapWithPrefixConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithPrefixConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Collections;
import java.util.Map;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java
index 09205f36e6..a0a2025bd5 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledConfigMapWithProfileConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Collections;
import java.util.Map;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledSecretWithPrefixConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithPrefixConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledSecretWithPrefixConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithPrefixConfigurationStub.java
index ea30c86b88..f5008b7363 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledSecretWithPrefixConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithPrefixConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Collections;
import java.util.Map;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledSecretWithProfileConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledSecretWithProfileConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java
index 813d37d54e..34b5632ef8 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/LabeledSecretWithProfileConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/LabeledSecretWithProfileConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedConfigMapWithPrefixConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedConfigMapWithPrefixConfigurationStub.java
similarity index 97%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedConfigMapWithPrefixConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedConfigMapWithPrefixConfigurationStub.java
index c021349cd1..aafb86d4a3 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedConfigMapWithPrefixConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedConfigMapWithPrefixConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Arrays;
import java.util.Collections;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedConfigMapWithProfileConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedConfigMapWithProfileConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedConfigMapWithProfileConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedConfigMapWithProfileConfigurationStub.java
index 8e2147c9ef..fdf2ad6d6b 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedConfigMapWithProfileConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedConfigMapWithProfileConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Arrays;
import java.util.Collections;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedSecretWithPrefixConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedSecretWithPrefixConfigurationStub.java
similarity index 97%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedSecretWithPrefixConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedSecretWithPrefixConfigurationStub.java
index dc8ab3dfd5..a6e41f64d8 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedSecretWithPrefixConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedSecretWithPrefixConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Arrays;
import java.util.Collections;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedSecretWithProfileConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedSecretWithProfileConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedSecretWithProfileConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedSecretWithProfileConfigurationStub.java
index b1e1a19542..3630a5f798 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/NamedSecretWithProfileConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/NamedSecretWithProfileConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.Arrays;
import java.util.Collections;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SingleSourceMultipleFilesConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/SingleSourceMultipleFilesConfigurationStub.java
similarity index 97%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SingleSourceMultipleFilesConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/SingleSourceMultipleFilesConfigurationStub.java
index e92948d747..c62d8a0164 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SingleSourceMultipleFilesConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/SingleSourceMultipleFilesConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.util.HashMap;
import java.util.Map;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/SourcesOrderConfigurationStub.java
similarity index 98%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/SourcesOrderConfigurationStub.java
index 9b6bd8587a..8f04ab4487 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap/stubs/SourcesOrderConfigurationStub.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap/stubs/SourcesOrderConfigurationStub.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap.stubs;
+package org.springframework.cloud.kubernetes.client.config.bootstrap.stubs;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesClientBootstrapConfigurationInsideK8s.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesClientBootstrapConfigurationInsideK8s.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesClientBootstrapConfigurationInsideK8s.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesClientBootstrapConfigurationInsideK8s.java
index 898d664fa9..7734050bad 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesClientBootstrapConfigurationInsideK8s.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesClientBootstrapConfigurationInsideK8s.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesClientBootstrapConfigurationNotInsideK8s.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesClientBootstrapConfigurationNotInsideK8s.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesClientBootstrapConfigurationNotInsideK8s.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesClientBootstrapConfigurationNotInsideK8s.java
index b84ef3c7e5..8b9e1e8da3 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesClientBootstrapConfigurationNotInsideK8s.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesClientBootstrapConfigurationNotInsideK8s.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesDisabled.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesDisabled.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesDisabled.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesDisabled.java
index bfe295c45d..c3c26557d3 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesDisabled.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesDisabled.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabled.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabled.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabled.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabled.java
index fc43330fe9..0978c87371 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabled.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabled.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledConfigDisabled.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledConfigDisabled.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledConfigDisabled.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledConfigDisabled.java
index e8ee40b554..298e3ec55e 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledConfigDisabled.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledConfigDisabled.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledOnPurpose.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledOnPurpose.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledOnPurpose.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledOnPurpose.java
index 821d15e2c1..8dcb00b509 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledOnPurpose.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledOnPurpose.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledSecretsAndConfigDisabled.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledSecretsAndConfigDisabled.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledSecretsAndConfigDisabled.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledSecretsAndConfigDisabled.java
index 52c8f9a515..01745a0312 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledSecretsAndConfigDisabled.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledSecretsAndConfigDisabled.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledSecretsDisabled.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledSecretsDisabled.java
similarity index 95%
rename from spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledSecretsDisabled.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledSecretsDisabled.java
index db268a9683..320e883277 100644
--- a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/boostrap_configuration/KubernetesEnabledSecretsDisabled.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/bootstrap_configurations/KubernetesEnabledSecretsDisabled.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.config.boostrap_configuration;
+package org.springframework.cloud.kubernetes.client.config.bootstrap_configurations;
import org.junit.jupiter.api.Test;
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/BootstrapKubernetesClientSanitizeConfigpropsEndpointTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/BootstrapKubernetesClientSanitizeConfigpropsEndpointTests.java
new file mode 100644
index 0000000000..585747b5a5
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/BootstrapKubernetesClientSanitizeConfigpropsEndpointTests.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2013-2023 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.sanitize_secrets;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.endpoint.SanitizableData;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalManagementPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * @author wind57
+ */
+class BootstrapKubernetesClientSanitizeConfigpropsEndpointTests {
+
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize",
+ "bootstrap.sanitize=true", "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class DefaultSettingsTest {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeSecretName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ // management.endpoint.configprops.show-values = NEVER
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize",
+ "management.endpoint.configprops.show-values=NEVER", "bootstrap.sanitize=true",
+ "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class ExplicitNever {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeSecretName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.configprops.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = false
+ *
+ * Sanitizing functions must apply, but we have none registered, as such
+ * everything is visible in plain text, both from configmaps and secrets.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize",
+ "management.endpoint.configprops.show-values=ALWAYS",
+ "spring.cloud.kubernetes.sanitize.secrets=false", "bootstrap.sanitize=true",
+ "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class AlwaysWithoutSanitizingFunction {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // secret is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeSecretName")
+ .isEqualTo("sanitizeSecretValue");
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.configprops.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = true
+ *
+ * Sanitizing functions must apply, and we have one registered, as such
+ * configmap is visible in plain text, but secrets are sanitized.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize-two",
+ "management.endpoint.configprops.show-values=ALWAYS",
+ "spring.cloud.kubernetes.sanitize.secrets=true", "bootstrap.sanitize=true",
+ "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class AlwaysWithSanitizingFunction {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // first secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeSecretName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // second secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.['sanitize-1'].beans.[*].properties.sanitizeSecretNameTwo")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/BootstrapKubernetesClientSanitizeEnvEndpointTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/BootstrapKubernetesClientSanitizeEnvEndpointTests.java
new file mode 100644
index 0000000000..98fca219b4
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/BootstrapKubernetesClientSanitizeEnvEndpointTests.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2013-2023 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.sanitize_secrets;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.endpoint.SanitizableData;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalManagementPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * @author wind57
+ */
+class BootstrapKubernetesClientSanitizeEnvEndpointTests {
+
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize",
+ "bootstrap.sanitize=true", "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class DefaultSettingsTest {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ // management.endpoint.env.show-values = NEVER
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize",
+ "management.endpoint.env.show-values=NEVER", "bootstrap.sanitize=true",
+ "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class ExplicitNever {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.env.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = false
+ *
+ * Sanitizing functions must apply, but we have none registered, as such
+ * everything is visible in plain text, both from configmaps and secrets.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize",
+ "management.endpoint.env.show-values=ALWAYS", "spring.cloud.kubernetes.sanitize.secrets=false",
+ "bootstrap.sanitize=true", "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class AlwaysWithoutSanitizingFunction {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // secret is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo("sanitizeSecretValue");
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.env.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = true
+ *
+ * Sanitizing functions must apply, and we have one registered, as such
+ * configmap is visible in plain text, but secrets are sanitized.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "spring.cloud.bootstrap.enabled=true",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.bootstrap.name=sanitize-two",
+ "management.endpoint.env.show-values=ALWAYS", "spring.cloud.kubernetes.sanitize.secrets=true",
+ "bootstrap.sanitize=true", "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class AlwaysWithSanitizingFunction {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // first secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // second secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretNameTwo'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataFabric8ConfigpropsEndpointTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataFabric8ConfigpropsEndpointTests.java
new file mode 100644
index 0000000000..c1100c04ce
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataFabric8ConfigpropsEndpointTests.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2013-2023 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.sanitize_secrets;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.endpoint.SanitizableData;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalManagementPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * @author wind57
+ */
+class ConfigDataFabric8ConfigpropsEndpointTests {
+
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "spring.config.import=kubernetes:,classpath:./sanitize.yaml" })
+ @Nested
+ class DefaultSettingsTest extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeSecretName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ // management.endpoint.configprops.show-values = NEVER
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "management.endpoint.configprops.show-values=NEVER",
+ "spring.config.import=kubernetes:,classpath:./sanitize.yaml" })
+ @Nested
+ class ExplicitNever extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeSecretName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.configprops.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = false
+ *
+ * Sanitizing functions must apply, but we have none registered, as such
+ * everything is visible in plain text, both from configmaps and secrets.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "management.endpoint.configprops.show-values=ALWAYS",
+ "spring.cloud.kubernetes.sanitize.secrets=false",
+ "spring.config.import=kubernetes:,classpath:./sanitize.yaml" })
+ @Nested
+ class AlwaysWithoutSanitizingFunction extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // secret is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeSecretName")
+ .isEqualTo("sanitizeSecretValue");
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.configprops.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = true
+ *
+ * Sanitizing functions must apply, and we have one registered, as such
+ * configmap is visible in plain text, but secrets are sanitized.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "management.endpoint.configprops.show-values=ALWAYS",
+ "spring.cloud.kubernetes.sanitize.secrets=true",
+ "spring.config.import=kubernetes:,classpath:./sanitize-two.yaml" })
+ @Nested
+ class AlwaysWithSanitizingFunction extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeConfigMapName")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // first secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeSecretName")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // second secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/configprops", this.port)
+ .accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody()
+ .jsonPath("contexts.sanitize.beans.[*].properties.sanitizeSecretNameTwo")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataKubernetesClientSanitizeEnvEndpointTests.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataKubernetesClientSanitizeEnvEndpointTests.java
new file mode 100644
index 0000000000..07156c3f1f
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataKubernetesClientSanitizeEnvEndpointTests.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2013-2023 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.sanitize_secrets;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.endpoint.SanitizableData;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalManagementPort;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * @author wind57
+ */
+class ConfigDataKubernetesClientSanitizeEnvEndpointTests {
+
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES",
+ "spring.config.import=kubernetes:,classpath:./sanitize.yaml",
+ "management.endpoints.web.exposure.include=*", "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class DefaultSettingsTest extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ // management.endpoint.env.show-values = NEVER
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "spring.config.import=kubernetes:,classpath:./sanitize.yaml",
+ "management.endpoint.env.show-values=NEVER", "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class ExplicitNever extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.env.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = false
+ *
+ * Sanitizing functions must apply, but we have none registered, as such
+ * everything is visible in plain text, both from configmaps and secrets.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "spring.config.import=kubernetes:,classpath:./sanitize.yaml",
+ "management.endpoint.env.show-values=ALWAYS", "spring.cloud.kubernetes.sanitize.secrets=false",
+ "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class AlwaysWithoutSanitizingFunction extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // secret is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo("sanitizeSecretValue");
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+ /**
+ *
+ * - management.endpoint.env.show-values = ALWAYS
+ * - spring.cloud.kubernetes.sanitize.secrets = true
+ *
+ * Sanitizing functions must apply, and we have one registered, as such
+ * configmap is visible in plain text, but secrets are sanitized.
+ *
+ *
+ */
+ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = SanitizeApp.class,
+ properties = { "spring.main.cloud-platform=KUBERNETES", "management.endpoints.web.exposure.include=*",
+ "spring.config.import=kubernetes:,classpath:./sanitize-two.yaml",
+ "management.endpoint.env.show-values=ALWAYS", "spring.cloud.kubernetes.sanitize.secrets=true",
+ "spring.cloud.kubernetes.client.namespace=test" })
+ @Nested
+ class AlwaysWithSanitizingFunction extends ConfigDataSanitize {
+
+ @Autowired
+ private WebTestClient webClient;
+
+ @LocalManagementPort
+ private int port;
+
+ @Test
+ void test() {
+ // configmap is not sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeConfigMapName'].value")
+ .isEqualTo("sanitizeConfigMapValue");
+
+ // first secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretName'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // second secret is sanitized
+ webClient.get().uri("http://localhost:{port}/actuator/env", this.port).accept(MediaType.APPLICATION_JSON)
+ .exchange().expectStatus().isOk().expectBody()
+ .jsonPath("propertySources.[*].properties.['sanitize.sanitizeSecretNameTwo'].value")
+ .isEqualTo(SanitizableData.SANITIZED_VALUE);
+
+ // secret is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/secret", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeSecretValue");
+
+ // configmap is usable from configuration properties
+ webClient.get().uri("http://localhost:{port}/configmap", this.port).exchange().expectStatus().isOk()
+ .expectBody().jsonPath("$").isEqualTo("sanitizeConfigMapValue");
+ }
+
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataSanitize.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataSanitize.java
new file mode 100644
index 0000000000..974e5ae181
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/ConfigDataSanitize.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013-2023 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.sanitize_secrets;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.WireMock;
+import io.kubernetes.client.util.ClientBuilder;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import org.springframework.cloud.kubernetes.client.KubernetesClientUtils;
+
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.mockito.Mockito.mockStatic;
+import static org.springframework.cloud.kubernetes.client.config.boostrap.stubs.BootstrapKubernetesClientSanitizeEnvEndpointStub.stubData;
+
+/**
+ * @author wind57
+ */
+abstract class ConfigDataSanitize {
+
+ private static MockedStatic clientUtilsMock;
+
+ @BeforeAll
+ static void wireMock() {
+ WireMockServer server = new WireMockServer(options().dynamicPort());
+ server.start();
+ WireMock.configureFor("localhost", server.port());
+ clientUtilsMock = mockStatic(KubernetesClientUtils.class);
+ clientUtilsMock.when(KubernetesClientUtils::kubernetesApiClient)
+ .thenReturn(new ClientBuilder().setBasePath(server.baseUrl()).build());
+ clientUtilsMock
+ .when(() -> KubernetesClientUtils.getApplicationNamespace(Mockito.any(), Mockito.any(), Mockito.any()))
+ .thenReturn("test");
+ stubData();
+ }
+
+ @AfterAll
+ static void teardown() {
+ clientUtilsMock.close();
+ }
+
+}
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-polling-reload/src/main/java/org/springframework/cloud/kubernetes/client/configmap/polling/reload/ConfigMapApp.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeApp.java
similarity index 81%
rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-polling-reload/src/main/java/org/springframework/cloud/kubernetes/client/configmap/polling/reload/ConfigMapApp.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeApp.java
index 08f7065777..22b88146e2 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-polling-reload/src/main/java/org/springframework/cloud/kubernetes/client/configmap/polling/reload/ConfigMapApp.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeApp.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.configmap.polling.reload;
+package org.springframework.cloud.kubernetes.client.config.sanitize_secrets;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -23,13 +23,12 @@
/**
* @author wind57
*/
-
+@EnableConfigurationProperties(SanitizeProperties.class)
@SpringBootApplication
-@EnableConfigurationProperties(ConfigMapProperties.class)
-public class ConfigMapApp {
+class SanitizeApp {
public static void main(String[] args) {
- SpringApplication.run(ConfigMapApp.class, args);
+ SpringApplication.run(SanitizeApp.class, args);
}
}
diff --git a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-polling-reload/src/main/java/org/springframework/cloud/kubernetes/client/configmap/polling/reload/ConfigMapController.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeController.java
similarity index 62%
rename from spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-polling-reload/src/main/java/org/springframework/cloud/kubernetes/client/configmap/polling/reload/ConfigMapController.java
rename to spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeController.java
index 7d8cbedfde..37d63a392e 100644
--- a/spring-cloud-kubernetes-integration-tests/spring-cloud-kubernetes-client-configmap-polling-reload/src/main/java/org/springframework/cloud/kubernetes/client/configmap/polling/reload/ConfigMapController.java
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.cloud.kubernetes.client.configmap.polling.reload;
+package org.springframework.cloud.kubernetes.client.config.sanitize_secrets;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -23,17 +23,22 @@
* @author wind57
*/
@RestController
-public class ConfigMapController {
+class SanitizeController {
- private final ConfigMapProperties properties;
+ private final SanitizeProperties sanitizeProperties;
- public ConfigMapController(ConfigMapProperties properties) {
- this.properties = properties;
+ SanitizeController(SanitizeProperties sanitizeProperties) {
+ this.sanitizeProperties = sanitizeProperties;
}
- @GetMapping("/key")
- public String key() {
- return properties.getKey();
+ @GetMapping("/secret")
+ String secret() {
+ return sanitizeProperties.getSanitizeSecretName();
+ }
+
+ @GetMapping("/configmap")
+ String configmap() {
+ return sanitizeProperties.getSanitizeConfigMapName();
}
}
diff --git a/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeProperties.java b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeProperties.java
new file mode 100644
index 0000000000..2ad2eaf224
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/java/org/springframework/cloud/kubernetes/client/config/sanitize_secrets/SanitizeProperties.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013-2023 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.sanitize_secrets;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author wind57
+ */
+@ConfigurationProperties("sanitize")
+class SanitizeProperties {
+
+ private String sanitizeSecretName;
+
+ private String sanitizeSecretNameTwo;
+
+ private String sanitizeConfigMapName;
+
+ public String getSanitizeSecretName() {
+ return sanitizeSecretName;
+ }
+
+ public void setSanitizeSecretName(String sanitizeSecretName) {
+ this.sanitizeSecretName = sanitizeSecretName;
+ }
+
+ public String getSanitizeConfigMapName() {
+ return sanitizeConfigMapName;
+ }
+
+ public void setSanitizeConfigMapName(String sanitizeConfigMapName) {
+ this.sanitizeConfigMapName = sanitizeConfigMapName;
+ }
+
+ public String getSanitizeSecretNameTwo() {
+ return sanitizeSecretNameTwo;
+ }
+
+ public void setSanitizeSecretNameTwo(String sanitizeSecretNameTwo) {
+ this.sanitizeSecretNameTwo = sanitizeSecretNameTwo;
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories b/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories
index 00abb43956..f453b1b3bd 100644
--- a/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories
+++ b/spring-cloud-kubernetes-client-config/src/test/resources/META-INF/spring.factories
@@ -1,14 +1,15 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithProfileConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedConfigMapWithPrefixConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithPrefixConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.NamedSecretWithProfileConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithPrefixConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledSecretWithProfileConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithPrefixConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.LabeledConfigMapWithProfileConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SingleSourceMultipleFilesConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.IncludeProfileSpecificSourcesConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.ConfigMapNameAsPrefixConfigurationStub, \
-org.springframework.cloud.kubernetes.client.config.boostrap.stubs.SourcesOrderConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithProfileConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedConfigMapWithPrefixConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedSecretWithPrefixConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.NamedSecretWithProfileConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledSecretWithPrefixConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledSecretWithProfileConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledConfigMapWithPrefixConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.LabeledConfigMapWithProfileConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SingleSourceMultipleFilesConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.IncludeProfileSpecificSourcesConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.ConfigMapNameAsPrefixConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.bootstrap.stubs.SourcesOrderConfigurationStub, \
+org.springframework.cloud.kubernetes.client.config.boostrap.stubs.BootstrapKubernetesClientSanitizeEnvEndpointStub, \
org.springframework.cloud.kubernetes.client.config.EnableRetryBootstrapConfiguration
diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/sanitize-two.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/sanitize-two.yaml
new file mode 100644
index 0000000000..7037403969
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/resources/sanitize-two.yaml
@@ -0,0 +1,16 @@
+spring:
+ application:
+ name: sanitize
+ cloud:
+ kubernetes:
+ secrets:
+ enableApi: true
+ sources:
+ - name: sanitize-secret
+ namespaces: test
+ - name: sanitize-secret-two
+ namespaces: test
+ config:
+ sources:
+ - name: sanitize-configmap
+ namespace: test
diff --git a/spring-cloud-kubernetes-client-config/src/test/resources/sanitize.yaml b/spring-cloud-kubernetes-client-config/src/test/resources/sanitize.yaml
new file mode 100644
index 0000000000..f91869d894
--- /dev/null
+++ b/spring-cloud-kubernetes-client-config/src/test/resources/sanitize.yaml
@@ -0,0 +1,14 @@
+spring:
+ application:
+ name: sanitize
+ cloud:
+ kubernetes:
+ secrets:
+ enableApi: true
+ sources:
+ - name: sanitize-secret
+ namespaces: test
+ config:
+ sources:
+ - name: sanitize-configmap
+ namespace: test
diff --git a/spring-cloud-kubernetes-client-discovery/pom.xml b/spring-cloud-kubernetes-client-discovery/pom.xml
index db48e67fca..5e7952df0f 100644
--- a/spring-cloud-kubernetes-client-discovery/pom.xml
+++ b/spring-cloud-kubernetes-client-discovery/pom.xml
@@ -5,7 +5,7 @@
spring-cloud-kubernetes
org.springframework.cloud
- 3.1.0-SNAPSHOT
+ 3.1.1-SNAPSHOT
4.0.0
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/ConditionalOnBlockingOrReactiveEnabled.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/ConditionalOnBlockingOrReactiveEnabled.java
index 6bb44aa493..1d53204690 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/ConditionalOnBlockingOrReactiveEnabled.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/ConditionalOnBlockingOrReactiveEnabled.java
@@ -26,12 +26,14 @@
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnBlockingOrReactiveDiscoveryEnabled;
import org.springframework.context.annotation.Conditional;
/**
* Conditional that is resolved to active when either
- * {@link ConditionalOnBlockingOrReactiveEnabled} or
+ * {@link ConditionalOnBlockingDiscoveryEnabled} or
* {@link ConditionalOnReactiveDiscoveryEnabled} matches.
+ * @deprecated in favor of {@link ConditionalOnBlockingOrReactiveDiscoveryEnabled}
*
* @author wind57
*/
@@ -40,6 +42,7 @@
@Documented
@Inherited
@Conditional(ConditionalOnBlockingOrReactiveEnabled.OnBlockingOrReactiveEnabled.class)
+@Deprecated(forRemoval = true)
public @interface ConditionalOnBlockingOrReactiveEnabled {
class OnBlockingOrReactiveEnabled extends AnyNestedCondition {
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/K8sInstanceIdHostPodNameSupplier.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/K8sInstanceIdHostPodNameSupplier.java
new file mode 100644
index 0000000000..49628ebd71
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/K8sInstanceIdHostPodNameSupplier.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import io.kubernetes.client.openapi.models.V1EndpointAddress;
+import io.kubernetes.client.openapi.models.V1ObjectReference;
+import io.kubernetes.client.openapi.models.V1Service;
+
+import org.springframework.cloud.kubernetes.commons.discovery.InstanceIdHostPodName;
+
+/**
+ * @author wind57
+ */
+final class K8sInstanceIdHostPodNameSupplier implements Supplier {
+
+ private final V1EndpointAddress endpointAddress;
+
+ private final V1Service service;
+
+ private K8sInstanceIdHostPodNameSupplier(V1EndpointAddress endpointAddress, V1Service service) {
+ this.endpointAddress = endpointAddress;
+ this.service = service;
+ }
+
+ @Override
+ public InstanceIdHostPodName get() {
+ return new InstanceIdHostPodName(instanceId(), host(), podName());
+ }
+
+ /**
+ * to be used when .spec.type of the Service is != 'ExternalName'.
+ */
+ static K8sInstanceIdHostPodNameSupplier nonExternalName(V1EndpointAddress endpointAddress, V1Service service) {
+ return new K8sInstanceIdHostPodNameSupplier(endpointAddress, service);
+ }
+
+ /**
+ * to be used when .spec.type of the Service is == 'ExternalName'.
+ */
+ static K8sInstanceIdHostPodNameSupplier externalName(V1Service service) {
+ return new K8sInstanceIdHostPodNameSupplier(null, service);
+ }
+
+ // instanceId is usually the pod-uid as seen in the .metadata.uid
+ private String instanceId() {
+ return Optional.ofNullable(endpointAddress).map(V1EndpointAddress::getTargetRef).map(V1ObjectReference::getUid)
+ .orElseGet(() -> service.getMetadata().getUid());
+ }
+
+ private String host() {
+ return Optional.ofNullable(endpointAddress).map(V1EndpointAddress::getIp)
+ .orElseGet(() -> service.getSpec().getExternalName());
+ }
+
+ private String podName() {
+ return Optional.ofNullable(endpointAddress).map(V1EndpointAddress::getTargetRef)
+ .filter(objectReference -> "Pod".equals(objectReference.getKind())).map(V1ObjectReference::getName)
+ .orElse(null);
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/K8sPodLabelsAndAnnotationsSupplier.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/K8sPodLabelsAndAnnotationsSupplier.java
new file mode 100644
index 0000000000..21c32f8f8a
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/K8sPodLabelsAndAnnotationsSupplier.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+import io.kubernetes.client.openapi.ApiException;
+import io.kubernetes.client.openapi.apis.CoreV1Api;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.cloud.kubernetes.commons.discovery.PodLabelsAndAnnotations;
+import org.springframework.core.log.LogAccessor;
+
+/**
+ * @author wind57
+ */
+final class K8sPodLabelsAndAnnotationsSupplier implements Function {
+
+ private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(K8sPodLabelsAndAnnotationsSupplier.class));
+
+ private final CoreV1Api coreV1Api;
+
+ private final String namespace;
+
+ private K8sPodLabelsAndAnnotationsSupplier(CoreV1Api coreV1Api, String namespace) {
+ this.coreV1Api = coreV1Api;
+ this.namespace = namespace;
+ }
+
+ /**
+ * to be used when .spec.type of the Service is != 'ExternalName'.
+ */
+ static K8sPodLabelsAndAnnotationsSupplier nonExternalName(CoreV1Api coreV1Api, String namespace) {
+ return new K8sPodLabelsAndAnnotationsSupplier(coreV1Api, namespace);
+ }
+
+ /**
+ * to be used when .spec.type of the Service is == 'ExternalName'.
+ */
+ static K8sPodLabelsAndAnnotationsSupplier externalName() {
+ return new K8sPodLabelsAndAnnotationsSupplier(null, null);
+ }
+
+ @Override
+ public PodLabelsAndAnnotations apply(String podName) {
+
+ V1ObjectMeta objectMeta;
+
+ try {
+ objectMeta = Optional.ofNullable(coreV1Api.readNamespacedPod(podName, namespace, null).getMetadata())
+ .orElse(new V1ObjectMetaBuilder().withLabels(Map.of()).withAnnotations(Map.of()).build());
+ }
+ catch (ApiException e) {
+ LOG.warn(e, "Could not get pod metadata");
+ objectMeta = new V1ObjectMetaBuilder().withLabels(Map.of()).withAnnotations(Map.of()).build();
+ }
+
+ return new PodLabelsAndAnnotations(Optional.ofNullable(objectMeta.getLabels()).orElse(Map.of()),
+ Optional.ofNullable(objectMeta.getAnnotations()).orElse(Map.of()));
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientConfigServerBootstrapper.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientConfigServerBootstrapper.java
index 282b843817..6976d08159 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientConfigServerBootstrapper.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientConfigServerBootstrapper.java
@@ -17,7 +17,6 @@
package org.springframework.cloud.kubernetes.client.discovery;
import java.util.Collections;
-import java.util.List;
import io.kubernetes.client.informer.SharedIndexInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
@@ -30,23 +29,19 @@
import io.kubernetes.client.util.Namespaces;
import io.kubernetes.client.util.generic.GenericKubernetesApi;
import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
-import org.springframework.boot.BootstrapContext;
import org.springframework.boot.BootstrapRegistry;
-import org.springframework.boot.context.properties.bind.BindHandler;
-import org.springframework.boot.context.properties.bind.Bindable;
-import org.springframework.boot.context.properties.bind.Binder;
-import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver;
+import org.springframework.cloud.config.client.ConfigServerConfigDataLocationResolver.PropertyResolver;
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesClientProperties;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.KubernetesConfigServerBootstrapper;
-import org.springframework.cloud.kubernetes.commons.config.KubernetesConfigServerInstanceProvider;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.Environment;
-import org.springframework.util.ClassUtils;
import static org.springframework.cloud.kubernetes.client.KubernetesClientUtils.kubernetesApiClient;
@@ -55,73 +50,53 @@
*/
class KubernetesClientConfigServerBootstrapper extends KubernetesConfigServerBootstrapper {
+ private static final Log LOG = LogFactory.getLog(KubernetesClientConfigServerBootstrapper.class);
+
@Override
public void initialize(BootstrapRegistry registry) {
- if (!ClassUtils.isPresent("org.springframework.cloud.config.client.ConfigServerInstanceProvider", null)) {
+ if (hasConfigServerInstanceProvider()) {
return;
}
- // We need to pass a lambda here rather than create a new instance of
- // ConfigServerInstanceProvider.Function
- // or else we will get ClassNotFoundExceptions if Spring Cloud Config is not on
- // the classpath
- registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, KubernetesFunction::create);
- }
-
- final static class KubernetesFunction implements ConfigServerInstanceProvider.Function {
-
- private final BootstrapContext context;
-
- private KubernetesFunction(BootstrapContext context) {
- this.context = context;
- }
-
- static KubernetesFunction create(BootstrapContext context) {
- return new KubernetesFunction(context);
- }
-
- @Override
- public List apply(String serviceId, Binder binder, BindHandler bindHandler, Log log) {
- if (binder == null || bindHandler == null || !getDiscoveryEnabled(binder, bindHandler)) {
- // If we don't have the Binder or BinderHandler from the
- // ConfigDataLocationResolverContext
- // we won't be able to create the necessary configuration
- // properties to configure the
- // Kubernetes DiscoveryClient
- return Collections.emptyList();
+ registry.registerIfAbsent(KubernetesDiscoveryProperties.class, context -> {
+ if (!getDiscoveryEnabled(context)) {
+ return null;
}
- KubernetesDiscoveryProperties discoveryProperties = createKubernetesDiscoveryProperties(binder,
- bindHandler);
- KubernetesClientProperties clientProperties = createKubernetesClientProperties(binder, bindHandler);
- return getInstanceProvider(discoveryProperties, clientProperties, context, binder, bindHandler, log)
- .getInstances(serviceId);
- }
+ return createKubernetesDiscoveryProperties(context);
+ });
- protected KubernetesConfigServerInstanceProvider getInstanceProvider(
- KubernetesDiscoveryProperties discoveryProperties, KubernetesClientProperties clientProperties,
- BootstrapContext context, Binder binder, BindHandler bindHandler, Log log) {
+ registry.registerIfAbsent(KubernetesClientProperties.class, context -> {
+ if (!getDiscoveryEnabled(context)) {
+ return null;
+ }
+ return createKubernetesClientProperties(context);
+ });
+ registry.registerIfAbsent(ConfigServerInstanceProvider.Function.class, context -> {
+ if (!getDiscoveryEnabled(context)) {
+ return (id) -> Collections.emptyList();
+ }
if (context.isRegistered(KubernetesInformerDiscoveryClient.class)) {
KubernetesInformerDiscoveryClient client = context.get(KubernetesInformerDiscoveryClient.class);
return client::getInstances;
}
else {
-
+ PropertyResolver propertyResolver = getPropertyResolver(context);
ApiClient defaultApiClient = kubernetesApiClient();
- defaultApiClient.setUserAgent(binder.bind("spring.cloud.kubernetes.client.user-agent", String.class)
- .orElse(KubernetesClientProperties.DEFAULT_USER_AGENT));
+ defaultApiClient.setUserAgent(propertyResolver.get("spring.cloud.kubernetes.client.user-agent",
+ String.class, KubernetesClientProperties.DEFAULT_USER_AGENT));
KubernetesClientAutoConfiguration clientAutoConfiguration = new KubernetesClientAutoConfiguration();
ApiClient apiClient = context.getOrElseSupply(ApiClient.class, () -> defaultApiClient);
KubernetesNamespaceProvider kubernetesNamespaceProvider = clientAutoConfiguration
- .kubernetesNamespaceProvider(getNamespaceEnvironment(binder, bindHandler));
-
+ .kubernetesNamespaceProvider(getNamespaceEnvironment(propertyResolver));
+ KubernetesDiscoveryProperties discoveryProperties = context.get(KubernetesDiscoveryProperties.class);
String namespace = getInformerNamespace(kubernetesNamespaceProvider, discoveryProperties);
SharedInformerFactory sharedInformerFactory = new SharedInformerFactory(apiClient);
- final GenericKubernetesApi servicesApi = new GenericKubernetesApi<>(
- V1Service.class, V1ServiceList.class, "", "v1", "services", apiClient);
+ GenericKubernetesApi servicesApi = new GenericKubernetesApi<>(V1Service.class,
+ V1ServiceList.class, "", "v1", "services", apiClient);
SharedIndexInformer serviceSharedIndexInformer = sharedInformerFactory
.sharedIndexInformerFor(servicesApi, V1Service.class, 0L, namespace);
Lister serviceLister = new Lister<>(serviceSharedIndexInformer.getIndexer());
- final GenericKubernetesApi endpointsApi = new GenericKubernetesApi<>(
+ GenericKubernetesApi endpointsApi = new GenericKubernetesApi<>(
V1Endpoints.class, V1EndpointsList.class, "", "v1", "endpoints", apiClient);
SharedIndexInformer endpointsSharedIndexInformer = sharedInformerFactory
.sharedIndexInformerFor(endpointsApi, V1Endpoints.class, 0L, namespace);
@@ -134,40 +109,32 @@ protected KubernetesConfigServerInstanceProvider getInstanceProvider(
return discoveryClient::getInstances;
}
catch (Exception e) {
- if (log != null) {
- log.warn("Error initiating informer discovery client", e);
- }
+ LOG.warn("Error initiating informer discovery client", e);
return (serviceId) -> Collections.emptyList();
}
finally {
sharedInformerFactory.stopAllRegisteredInformers();
}
}
- }
+ });
- private String getInformerNamespace(KubernetesNamespaceProvider kubernetesNamespaceProvider,
- KubernetesDiscoveryProperties discoveryProperties) {
- return discoveryProperties.allNamespaces() ? Namespaces.NAMESPACE_ALL
- : kubernetesNamespaceProvider.getNamespace() == null ? Namespaces.NAMESPACE_DEFAULT
- : kubernetesNamespaceProvider.getNamespace();
- }
-
- private Environment getNamespaceEnvironment(Binder binder, BindHandler bindHandler) {
- return new AbstractEnvironment() {
- @Override
- public String getProperty(String key) {
- return binder.bind(key, Bindable.of(String.class), bindHandler).orElse(super.getProperty(key));
- }
- };
- }
+ }
- // This method should never be called, but is there for backward
- // compatibility purposes
- @Override
- public List apply(String serviceId) {
- return apply(serviceId, null, null, null);
- }
+ private String getInformerNamespace(KubernetesNamespaceProvider kubernetesNamespaceProvider,
+ KubernetesDiscoveryProperties discoveryProperties) {
+ return discoveryProperties.allNamespaces() ? Namespaces.NAMESPACE_ALL
+ : kubernetesNamespaceProvider.getNamespace() == null ? Namespaces.NAMESPACE_DEFAULT
+ : kubernetesNamespaceProvider.getNamespace();
+ }
+ private Environment getNamespaceEnvironment(
+ ConfigServerConfigDataLocationResolver.PropertyResolver propertyResolver) {
+ return new AbstractEnvironment() {
+ @Override
+ public String getProperty(String key) {
+ return propertyResolver.get(key, String.class, super.getProperty(key));
+ }
+ };
}
}
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerAutoConfiguration.java
index 2b73621095..b81d96f1db 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerAutoConfiguration.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerAutoConfiguration.java
@@ -38,6 +38,7 @@
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.config.NamespaceResolutionFailedException;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnBlockingOrReactiveDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
@@ -56,7 +57,7 @@
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnKubernetesDiscoveryEnabled
-@ConditionalOnBlockingOrReactiveEnabled
+@ConditionalOnBlockingOrReactiveDiscoveryEnabled
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
@Conditional(ConditionalOnSelectiveNamespacesMissing.class)
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfiguration.java
index 5f44ed9b21..23f1ed26a2 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfiguration.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfiguration.java
@@ -39,6 +39,7 @@
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnBlockingOrReactiveDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
@@ -56,7 +57,7 @@
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnKubernetesDiscoveryEnabled
-@ConditionalOnBlockingOrReactiveEnabled
+@ConditionalOnBlockingOrReactiveDiscoveryEnabled
@Conditional(ConditionalOnSelectiveNamespacesPresent.class)
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
@@ -67,10 +68,8 @@ public class KubernetesClientInformerSelectiveNamespacesAutoConfiguration {
LogFactory.getLog(KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class));
// we rely on the order of namespaces to enable listers, as such provide a bean of
- // namespaces
- // as a list, instead of the incoming Set.
+ // namespaces as a list, instead of the incoming Set.
@Bean
- @ConditionalOnMissingBean
public List selectiveNamespaces(KubernetesDiscoveryProperties properties) {
List selectiveNamespaces = properties.namespaces().stream().sorted().toList();
LOG.debug(() -> "using selective namespaces : " + selectiveNamespaces);
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientUtils.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientUtils.java
index 9e0a88aeae..864d469dc5 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientUtils.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientUtils.java
@@ -17,15 +17,19 @@
package org.springframework.cloud.kubernetes.client.discovery;
import java.time.Duration;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.informer.cache.Lister;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
+import io.kubernetes.client.openapi.models.V1EndpointAddress;
+import io.kubernetes.client.openapi.models.V1EndpointSubset;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.openapi.models.V1Service;
import io.kubernetes.client.openapi.models.V1ServiceSpec;
@@ -33,14 +37,15 @@
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
+import org.springframework.cloud.kubernetes.commons.discovery.ServiceMetadata;
import org.springframework.core.log.LogAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
+import org.springframework.util.CollectionUtils;
-import static org.springframework.cloud.kubernetes.commons.config.ConfigUtils.keysWithPrefix;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.NAMESPACE_METADATA_KEY;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.SERVICE_TYPE;
+import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.UNSET_PORT_NAME;
+import static org.springframework.util.StringUtils.hasText;
/**
* @author wind57
@@ -82,40 +87,6 @@ static boolean matchesServiceLabels(V1Service service, KubernetesDiscoveryProper
}
- /**
- * This adds the following metadata.
- * - labels (if requested)
- * - annotations (if requested)
- * - metadata
- * - service type
- *
- */
- static Map serviceMetadata(KubernetesDiscoveryProperties properties, V1Service service,
- String serviceId) {
-
- Map serviceMetadata = new HashMap<>();
- KubernetesDiscoveryProperties.Metadata metadataProps = properties.metadata();
- if (metadataProps.addLabels()) {
- Map labelMetadata = keysWithPrefix(service.getMetadata().getLabels(),
- metadataProps.labelsPrefix());
- LOG.debug(() -> "Adding labels metadata: " + labelMetadata + " for serviceId: " + serviceId);
- serviceMetadata.putAll(labelMetadata);
- }
- if (metadataProps.addAnnotations()) {
- Map annotationMetadata = keysWithPrefix(service.getMetadata().getAnnotations(),
- metadataProps.annotationsPrefix());
- LOG.debug(() -> "Adding annotations metadata: " + annotationMetadata + " for serviceId: " + serviceId);
- serviceMetadata.putAll(annotationMetadata);
- }
-
- serviceMetadata.put(NAMESPACE_METADATA_KEY,
- Optional.ofNullable(service.getMetadata()).map(V1ObjectMeta::getNamespace).orElse(null));
- serviceMetadata.put(SERVICE_TYPE,
- Optional.ofNullable(service.getSpec()).map(V1ServiceSpec::getType).orElse(null));
-
- return serviceMetadata;
- }
-
static Predicate filter(KubernetesDiscoveryProperties properties) {
String spelExpression = properties.filter();
Predicate predicate;
@@ -159,4 +130,38 @@ static void postConstruct(List sharedInformerFactories,
}
+ static ServiceMetadata serviceMetadata(V1Service service) {
+ V1ObjectMeta metadata = service.getMetadata();
+ V1ServiceSpec serviceSpec = service.getSpec();
+ return new ServiceMetadata(metadata.getName(), metadata.getNamespace(), serviceSpec.getType(),
+ metadata.getLabels(), metadata.getAnnotations());
+ }
+
+ /**
+ * a service is allowed to have a single port defined without a name.
+ */
+ static Map endpointSubsetsPortData(List endpointSubsets) {
+ return endpointSubsets.stream()
+ .flatMap(endpointSubset -> Optional.ofNullable(endpointSubset.getPorts()).orElse(List.of()).stream())
+ .collect(Collectors.toMap(
+ endpointPort -> hasText(endpointPort.getName()) ? endpointPort.getName() : UNSET_PORT_NAME,
+ CoreV1EndpointPort::getPort));
+ }
+
+ static List addresses(V1EndpointSubset endpointSubset,
+ KubernetesDiscoveryProperties properties) {
+ List addresses = Optional.ofNullable(endpointSubset.getAddresses()).map(ArrayList::new)
+ .orElse(new ArrayList<>());
+
+ if (properties.includeNotReadyAddresses()) {
+ List notReadyAddresses = endpointSubset.getNotReadyAddresses();
+ if (CollectionUtils.isEmpty(notReadyAddresses)) {
+ return addresses;
+ }
+ addresses.addAll(notReadyAddresses);
+ }
+
+ return addresses;
+ }
+
}
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerAutoConfiguration.java
index e504eb3219..6480085976 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerAutoConfiguration.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerAutoConfiguration.java
@@ -37,6 +37,7 @@
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnBlockingOrReactiveDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesDiscoveryEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
@@ -58,7 +59,7 @@
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnKubernetesDiscoveryEnabled
-@ConditionalOnBlockingOrReactiveEnabled
+@ConditionalOnBlockingOrReactiveDiscoveryEnabled
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
@Conditional(ConditionalOnSelectiveNamespacesMissing.class)
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClient.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClient.java
index 572ecd3510..251d4a3a56 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClient.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClient.java
@@ -17,43 +17,47 @@
package org.springframework.cloud.kubernetes.client.discovery;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import io.kubernetes.client.informer.SharedInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.informer.cache.Lister;
-import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
+import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.V1EndpointAddress;
+import io.kubernetes.client.openapi.models.V1EndpointSubset;
import io.kubernetes.client.openapi.models.V1Endpoints;
import io.kubernetes.client.openapi.models.V1Service;
import jakarta.annotation.PostConstruct;
import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
-import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
+import org.springframework.cloud.kubernetes.commons.discovery.ServiceMetadata;
+import org.springframework.cloud.kubernetes.commons.discovery.ServicePortNameAndNumber;
+import org.springframework.cloud.kubernetes.commons.discovery.ServicePortSecureResolver;
import org.springframework.core.log.LogAccessor;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
+import static org.springframework.cloud.kubernetes.client.discovery.K8sInstanceIdHostPodNameSupplier.externalName;
+import static org.springframework.cloud.kubernetes.client.discovery.K8sInstanceIdHostPodNameSupplier.nonExternalName;
+import static org.springframework.cloud.kubernetes.client.discovery.K8sPodLabelsAndAnnotationsSupplier.externalName;
+import static org.springframework.cloud.kubernetes.client.discovery.K8sPodLabelsAndAnnotationsSupplier.nonExternalName;
+import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.addresses;
+import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.endpointSubsetsPortData;
import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.filter;
import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.matchesServiceLabels;
import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.postConstruct;
import static org.springframework.cloud.kubernetes.client.discovery.KubernetesDiscoveryClientUtils.serviceMetadata;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTP;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.HTTPS;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.PRIMARY_PORT_NAME_LABEL_KEY;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.SECURED;
-import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.UNSET_PORT_NAME;
+import static org.springframework.cloud.kubernetes.commons.discovery.DiscoveryClientUtils.endpointsPort;
+import static org.springframework.cloud.kubernetes.commons.discovery.DiscoveryClientUtils.serviceInstance;
+import static org.springframework.cloud.kubernetes.commons.discovery.DiscoveryClientUtils.serviceInstanceMetadata;
+import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.EXTERNAL_NAME;
/**
* @author Min Kim
@@ -76,6 +80,13 @@ public class KubernetesInformerDiscoveryClient implements DiscoveryClient {
private final Predicate filter;
+ private final ServicePortSecureResolver servicePortSecureResolver;
+
+ // visible only for testing and
+ // must be constructor injected in a future release
+ @Autowired
+ CoreV1Api coreV1Api;
+
@Deprecated(forRemoval = true)
public KubernetesInformerDiscoveryClient(String namespace, SharedInformerFactory sharedInformerFactory,
Lister serviceLister, Lister endpointsLister,
@@ -87,6 +98,7 @@ public KubernetesInformerDiscoveryClient(String namespace, SharedInformerFactory
this.informersReadyFunc = () -> serviceInformer.hasSynced() && endpointsInformer.hasSynced();
this.properties = properties;
filter = filter(properties);
+ servicePortSecureResolver = new ServicePortSecureResolver(properties);
}
public KubernetesInformerDiscoveryClient(SharedInformerFactory sharedInformerFactory,
@@ -99,6 +111,7 @@ public KubernetesInformerDiscoveryClient(SharedInformerFactory sharedInformerFac
this.informersReadyFunc = () -> serviceInformer.hasSynced() && endpointsInformer.hasSynced();
this.properties = properties;
filter = filter(properties);
+ servicePortSecureResolver = new ServicePortSecureResolver(properties);
}
public KubernetesInformerDiscoveryClient(List sharedInformerFactories,
@@ -119,6 +132,7 @@ public KubernetesInformerDiscoveryClient(List sharedInfor
this.properties = properties;
filter = filter(properties);
+ servicePortSecureResolver = new ServicePortSecureResolver(properties);
}
@Override
@@ -130,104 +144,76 @@ public String description() {
public List getInstances(String serviceId) {
Objects.requireNonNull(serviceId, "serviceId must be provided");
- List services = serviceListers.stream().flatMap(x -> x.list().stream())
+ List allServices = serviceListers.stream().flatMap(x -> x.list().stream())
.filter(scv -> scv.getMetadata() != null).filter(svc -> serviceId.equals(svc.getMetadata().getName()))
- .filter(scv -> matchesServiceLabels(scv, properties)).filter(filter).toList();
- return services.stream().flatMap(service -> getServiceInstanceDetails(service, serviceId)).toList();
+ .filter(scv -> matchesServiceLabels(scv, properties)).toList();
+
+ List serviceInstances = allServices.stream().filter(filter)
+ .flatMap(service -> serviceInstances(service, serviceId).stream())
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ if (properties.includeExternalNameServices()) {
+ LOG.debug(() -> "Searching for 'ExternalName' type of services with serviceId : " + serviceId);
+ List externalNameServices = allServices.stream().filter(s -> s.getSpec() != null)
+ .filter(s -> EXTERNAL_NAME.equals(s.getSpec().getType())).toList();
+ for (V1Service service : externalNameServices) {
+ ServiceMetadata serviceMetadata = serviceMetadata(service);
+ Map serviceInstanceMetadata = serviceInstanceMetadata(Map.of(), serviceMetadata,
+ properties);
+
+ K8sInstanceIdHostPodNameSupplier supplierOne = externalName(service);
+ K8sPodLabelsAndAnnotationsSupplier supplierTwo = externalName();
+
+ ServiceInstance externalNameServiceInstance = serviceInstance(null, serviceMetadata, supplierOne,
+ supplierTwo, new ServicePortNameAndNumber(-1, null), serviceInstanceMetadata, properties);
+ serviceInstances.add(externalNameServiceInstance);
+ }
+ }
+
+ return serviceInstances;
}
- private Stream getServiceInstanceDetails(V1Service service, String serviceId) {
- Map serviceMetadata = serviceMetadata(properties, service, serviceId);
+ private List serviceInstances(V1Service service, String serviceId) {
- List endpoints = endpointsListers.stream()
- .map(endpointsLister -> endpointsLister.namespace(service.getMetadata().getNamespace())
- .get(service.getMetadata().getName()))
- .filter(Objects::nonNull).filter(ep -> ep.getSubsets() != null).toList();
+ List instances = new ArrayList<>();
- Optional discoveredPrimaryPortName = Optional.empty();
- if (service.getMetadata() != null && service.getMetadata().getLabels() != null) {
- discoveredPrimaryPortName = Optional
- .ofNullable(service.getMetadata().getLabels().get(PRIMARY_PORT_NAME_LABEL_KEY));
- }
- final String primaryPortName = discoveredPrimaryPortName.orElse(properties.primaryPortName());
-
- final boolean secured = isSecured(service);
-
- return endpoints.stream()
- .flatMap(ep -> ep.getSubsets().stream()
- .filter(subset -> subset.getPorts() != null && subset.getPorts().size() > 0) // safeguard
- .flatMap(subset -> {
- Map metadata = new HashMap<>(serviceMetadata);
- List endpointPorts = subset.getPorts();
- if (properties.metadata() != null && properties.metadata().addPorts()) {
- endpointPorts.forEach(p -> metadata.put(
- StringUtils.hasText(p.getName()) ? p.getName() : UNSET_PORT_NAME,
- Integer.toString(p.getPort())));
- }
- List addresses = subset.getAddresses();
- if (addresses == null) {
- addresses = new ArrayList<>();
- }
- if (properties.includeNotReadyAddresses()
- && !CollectionUtils.isEmpty(subset.getNotReadyAddresses())) {
- addresses.addAll(subset.getNotReadyAddresses());
- }
-
- final int port = findEndpointPort(endpointPorts, primaryPortName, serviceId);
- return addresses.stream()
- .map(addr -> new DefaultKubernetesServiceInstance(
- addr.getTargetRef() != null ? addr.getTargetRef().getUid() : "", serviceId,
- addr.getIp(), port, metadata, secured, service.getMetadata().getNamespace(),
- // TODO find out how to get cluster name
- // possibly from
- // KubeConfig
- null));
- }));
- }
+ List allEndpoints = endpointsListers.stream()
+ .map(endpointsLister -> endpointsLister.namespace(service.getMetadata().getNamespace()).get(serviceId))
+ .filter(Objects::nonNull).toList();
- private static boolean isSecured(V1Service service) {
- Optional securedOpt = Optional.empty();
- if (service.getMetadata() != null && service.getMetadata().getAnnotations() != null) {
- securedOpt = Optional.ofNullable(service.getMetadata().getAnnotations().get(SECURED));
- }
- if (!securedOpt.isPresent() && service.getMetadata() != null && service.getMetadata().getLabels() != null) {
- securedOpt = Optional.ofNullable(service.getMetadata().getLabels().get(SECURED));
- }
- return Boolean.parseBoolean(securedOpt.orElse("false"));
- }
+ for (V1Endpoints endpoints : allEndpoints) {
+ List subsets = endpoints.getSubsets();
+ if (subsets == null || subsets.isEmpty()) {
+ LOG.debug(() -> "serviceId : " + serviceId + " does not have any subsets");
+ }
+ else {
+ ServiceMetadata serviceMetadata = serviceMetadata(service);
+ Map portsData = endpointSubsetsPortData(subsets);
+ Map serviceInstanceMetadata = serviceInstanceMetadata(portsData, serviceMetadata,
+ properties);
- private int findEndpointPort(List endpointPorts, String primaryPortName, String serviceId) {
- if (endpointPorts.size() == 1) {
- return endpointPorts.get(0).getPort();
- }
- else {
- Map ports = endpointPorts.stream().filter(p -> StringUtils.hasText(p.getName()))
- .collect(Collectors.toMap(CoreV1EndpointPort::getName, CoreV1EndpointPort::getPort));
- // This oneliner is looking for a port with a name equal to the primary port
- // name specified in the service label
- // or in spring.cloud.kubernetes.discovery.primary-port-name, equal to https,
- // or equal to http.
- // In case no port has been found return -1 to log a warning and fall back to
- // the first port in the list.
- int discoveredPort = ports.getOrDefault(primaryPortName,
- ports.getOrDefault(HTTPS, ports.getOrDefault(HTTP, -1)));
-
- if (discoveredPort == -1) {
- if (StringUtils.hasText(primaryPortName)) {
- LOG.warn(() -> "Could not find a port named '" + primaryPortName
- + "', 'https', or 'http' for service '" + serviceId + "'.");
- }
- else {
- LOG.warn(() -> "Could not find a port named 'https' or 'http' for service '" + serviceId + "'.");
+ for (V1EndpointSubset endpointSubset : subsets) {
+
+ Map endpointsPortData = endpointSubsetsPortData(List.of(endpointSubset));
+ ServicePortNameAndNumber portData = endpointsPort(endpointsPortData, serviceMetadata, properties);
+
+ List addresses = addresses(endpointSubset, properties);
+ for (V1EndpointAddress endpointAddress : addresses) {
+
+ K8sInstanceIdHostPodNameSupplier supplierOne = nonExternalName(endpointAddress, service);
+ K8sPodLabelsAndAnnotationsSupplier supplierTwo = nonExternalName(coreV1Api,
+ service.getMetadata().getNamespace());
+
+ ServiceInstance serviceInstance = serviceInstance(servicePortSecureResolver, serviceMetadata,
+ supplierOne, supplierTwo, portData, serviceInstanceMetadata, properties);
+ instances.add(serviceInstance);
+ }
}
- LOG.warn(
- () -> "Make sure that either the primary-port-name label has been added to the service, or that spring.cloud.kubernetes.discovery.primary-port-name has been configured.");
- LOG.warn(() -> "Alternatively name the primary port 'https' or 'http'");
- LOG.warn(() -> "An incorrect configuration may result in non-deterministic behaviour.");
- discoveredPort = endpointPorts.get(0).getPort();
+
}
- return discoveredPort;
}
+
+ return instances;
}
@Override
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClientAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClientAutoConfiguration.java
index 57341b29b9..5a08ebd0b3 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClientAutoConfiguration.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesInformerDiscoveryClientAutoConfiguration.java
@@ -25,22 +25,16 @@
import io.kubernetes.client.openapi.models.V1Service;
import org.apache.commons.logging.LogFactory;
-import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
-import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
-import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
-import org.springframework.cloud.client.ConditionalOnDiscoveryHealthIndicatorEnabled;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.PodUtils;
-import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesDiscoveryEnabled;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnSpringCloudKubernetesBlockingDiscovery;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnSpringCloudKubernetesBlockingDiscoveryHealthInitializer;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
@@ -54,10 +48,7 @@
* @author wind57
*/
@Configuration(proxyBeanMethods = false)
-@ConditionalOnDiscoveryEnabled
-@ConditionalOnKubernetesDiscoveryEnabled
-@ConditionalOnBlockingDiscoveryEnabled
-@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
+@ConditionalOnSpringCloudKubernetesBlockingDiscovery
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class })
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class,
KubernetesClientInformerAutoConfiguration.class,
@@ -84,8 +75,7 @@ public KubernetesInformerDiscoveryClient kubernetesInformerDiscoveryClient(
* bean of type DiscoveryClientHealthIndicator via ObjectProvider.
*/
@Bean
- @ConditionalOnClass({ HealthIndicator.class })
- @ConditionalOnDiscoveryHealthIndicatorEnabled
+ @ConditionalOnSpringCloudKubernetesBlockingDiscoveryHealthInitializer
public KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(
ApplicationEventPublisher applicationEventPublisher, PodUtils> podUtils) {
LOG.debug(() -> "Will publish InstanceRegisteredEvent from blocking implementation");
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatch.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatch.java
index 2a3d5d44fb..bb9fbe5c67 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatch.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatch.java
@@ -36,6 +36,7 @@
import org.springframework.core.log.LogAccessor;
import org.springframework.scheduling.annotation.Scheduled;
+import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.CATALOG_WATCH_PROPERTY_WITH_DEFAULT_VALUE;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.DISCOVERY_GROUP;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.DISCOVERY_VERSION;
import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryConstants.ENDPOINT_SLICE;
@@ -67,7 +68,7 @@ public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
- @Scheduled(fixedDelayString = "${spring.cloud.kubernetes.discovery.catalogServicesWatchDelay:30000}")
+ @Scheduled(fixedDelayString = "${" + CATALOG_WATCH_PROPERTY_WITH_DEFAULT_VALUE + "}")
void catalogServicesWatch() {
try {
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatchAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatchAutoConfiguration.java
index c1b1e8cd10..be874dce08 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatchAutoConfiguration.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesCatalogWatchAutoConfiguration.java
@@ -20,13 +20,10 @@
import io.kubernetes.client.openapi.apis.CoreV1Api;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.cloud.CloudPlatform;
-import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.kubernetes.client.KubernetesClientAutoConfiguration;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
-import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesCatalogEnabled;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesCatalogWatcherEnabled;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
import org.springframework.context.annotation.Bean;
@@ -39,9 +36,7 @@
* @author wind57
*/
@Configuration(proxyBeanMethods = false)
-@ConditionalOnDiscoveryEnabled
-@ConditionalOnKubernetesCatalogEnabled
-@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
+@ConditionalOnKubernetesCatalogWatcherEnabled
@AutoConfigureAfter({ KubernetesClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class })
class KubernetesCatalogWatchAutoConfiguration {
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointSlicesCatalogWatch.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointSlicesCatalogWatch.java
index 46deca3d1a..1d14db25f5 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointSlicesCatalogWatch.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointSlicesCatalogWatch.java
@@ -81,7 +81,7 @@ else if (!context.properties().namespaces().isEmpty()) {
private List endpointSlices(DiscoveryV1Api api, Map labels) {
try {
return api.listEndpointSliceForAllNamespaces(null, null, null, labelSelector(labels), null, null, null,
- null, null, null).getItems();
+ null, null, null, null).getItems();
}
catch (ApiException e) {
LOG.warn(e, () -> "can not list endpoint slices in all namespaces");
@@ -93,7 +93,7 @@ private List namespacedEndpointSlices(DiscoveryV1Api api, Strin
Map labels) {
try {
return api.listNamespacedEndpointSlice(namespace, null, null, null, null, labelSelector(labels), null, null,
- null, null, null).getItems();
+ null, null, null, null).getItems();
}
catch (ApiException e) {
LOG.warn(e, () -> "can not list endpoint slices in namespace " + namespace);
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointsCatalogWatch.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointsCatalogWatch.java
index 3409e58d19..3bac70afb4 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointsCatalogWatch.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/catalog/KubernetesEndpointsCatalogWatch.java
@@ -91,7 +91,7 @@ else if (!context.properties().namespaces().isEmpty()) {
private List endpoints(CoreV1Api client, Map labels) {
try {
return client.listEndpointsForAllNamespaces(null, null, null, labelSelector(labels), null, null, null, null,
- null, null).getItems();
+ null, null, null).getItems();
}
catch (ApiException e) {
LOG.warn(e, () -> "can not list endpoints in all namespaces");
@@ -102,7 +102,7 @@ private List endpoints(CoreV1Api client, Map labels
private List namespacedEndpoints(CoreV1Api client, String namespace, Map labels) {
try {
return client.listNamespacedEndpoints(namespace, null, null, null, null, labelSelector(labels), null, null,
- null, null, null).getItems();
+ null, null, null, null).getItems();
}
catch (ApiException e) {
LOG.warn(e, () -> "can not list endpoints in namespace " + namespace);
diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/reactive/KubernetesInformerReactiveDiscoveryClientAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/reactive/KubernetesInformerReactiveDiscoveryClientAutoConfiguration.java
index 62f7393912..9d7f1b40e9 100644
--- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/reactive/KubernetesInformerReactiveDiscoveryClientAutoConfiguration.java
+++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/reactive/KubernetesInformerReactiveDiscoveryClientAutoConfiguration.java
@@ -27,13 +27,7 @@
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.cloud.CloudPlatform;
-import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
-import org.springframework.cloud.client.ConditionalOnDiscoveryHealthIndicatorEnabled;
-import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration;
import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration;
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
@@ -48,7 +42,8 @@
import org.springframework.cloud.kubernetes.client.discovery.KubernetesInformerDiscoveryClient;
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
import org.springframework.cloud.kubernetes.commons.PodUtils;
-import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnKubernetesDiscoveryEnabled;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnSpringCloudKubernetesReactiveDiscovery;
+import org.springframework.cloud.kubernetes.commons.discovery.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration;
@@ -65,10 +60,7 @@
*/
@Configuration(proxyBeanMethods = false)
-@ConditionalOnDiscoveryEnabled
-@ConditionalOnKubernetesDiscoveryEnabled
-@ConditionalOnReactiveDiscoveryEnabled
-@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
+@ConditionalOnSpringCloudKubernetesReactiveDiscovery
@AutoConfigureBefore({ SimpleReactiveDiscoveryClientAutoConfiguration.class,
ReactiveCommonsClientAutoConfiguration.class })
@AutoConfigureAfter({ ReactiveCompositeDiscoveryClientAutoConfiguration.class,
@@ -106,8 +98,7 @@ public KubernetesInformerReactiveDiscoveryClient kubernetesReactiveDiscoveryClie
* Post an event so that health indicator is initialized.
*/
@Bean
- @ConditionalOnClass(name = "org.springframework.boot.actuate.health.ReactiveHealthIndicator")
- @ConditionalOnDiscoveryHealthIndicatorEnabled
+ @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer(
ApplicationEventPublisher applicationEventPublisher, PodUtils> podUtils) {
LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation");
@@ -118,8 +109,7 @@ KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer
* unlike the blocking implementation, we need to register the health indicator.
*/
@Bean
- @ConditionalOnClass(name = "org.springframework.boot.actuate.health.ReactiveHealthIndicator")
- @ConditionalOnDiscoveryHealthIndicatorEnabled
+ @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer
ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator(
KubernetesInformerReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties) {
return new ReactiveDiscoveryClientHealthIndicator(client, properties);
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/K8sInstanceIdHostPodNameSupplierTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/K8sInstanceIdHostPodNameSupplierTests.java
new file mode 100644
index 0000000000..fd9ce80b59
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/K8sInstanceIdHostPodNameSupplierTests.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import io.kubernetes.client.openapi.models.V1EndpointAddress;
+import io.kubernetes.client.openapi.models.V1EndpointAddressBuilder;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
+import io.kubernetes.client.openapi.models.V1ObjectReferenceBuilder;
+import io.kubernetes.client.openapi.models.V1Service;
+import io.kubernetes.client.openapi.models.V1ServiceBuilder;
+import io.kubernetes.client.openapi.models.V1ServiceSpecBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.cloud.kubernetes.commons.discovery.InstanceIdHostPodName;
+
+/**
+ * @author wind57
+ */
+class K8sInstanceIdHostPodNameSupplierTests {
+
+ @Test
+ void instanceIdNoEndpointAddress() {
+ V1Service service = new V1ServiceBuilder().withSpec(new V1ServiceSpecBuilder().build())
+ .withMetadata(new V1ObjectMetaBuilder().withUid("123").build()).build();
+
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.externalName(service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(result.instanceId(), "123");
+ }
+
+ @Test
+ void instanceIdWithEndpointAddress() {
+ V1EndpointAddress endpointAddress = new V1EndpointAddressBuilder()
+ .withTargetRef(new V1ObjectReferenceBuilder().withUid("456").build()).build();
+ V1Service service = new V1ServiceBuilder().withSpec(new V1ServiceSpecBuilder().build())
+ .withMetadata(new V1ObjectMetaBuilder().withUid("123").build()).build();
+
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.nonExternalName(endpointAddress,
+ service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(result.instanceId(), "456");
+ }
+
+ @Test
+ void hostNoEndpointAddress() {
+ V1Service service = new V1ServiceBuilder()
+ .withSpec(new V1ServiceSpecBuilder().withExternalName("external-name").build())
+ .withMetadata(new V1ObjectMeta()).build();
+
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.externalName(service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(result.host(), "external-name");
+ }
+
+ @Test
+ void hostWithEndpointAddress() {
+ V1EndpointAddress endpointAddress = new V1EndpointAddressBuilder().withIp("127.0.0.1").build();
+ V1Service service = new V1ServiceBuilder()
+ .withSpec(new V1ServiceSpecBuilder().withExternalName("external-name").build())
+ .withMetadata(new V1ObjectMeta()).build();
+
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.nonExternalName(endpointAddress,
+ service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(result.host(), "127.0.0.1");
+ }
+
+ @Test
+ void testPodNameIsNull() {
+ V1Service service = new V1ServiceBuilder().withMetadata(new V1ObjectMetaBuilder().withUid("123").build())
+ .withSpec(new V1ServiceSpecBuilder().withExternalName("external-name").build()).build();
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.externalName(service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertNull(result.podName());
+ }
+
+ @Test
+ void podNameKindNotPod() {
+ V1EndpointAddress endpointAddress = new V1EndpointAddressBuilder()
+ .withTargetRef(new V1ObjectReferenceBuilder().withKind("Service").build()).build();
+ V1Service service = new V1ServiceBuilder()
+ .withSpec(new V1ServiceSpecBuilder().withExternalName("external-name").build())
+ .withMetadata(new V1ObjectMeta()).build();
+
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.nonExternalName(endpointAddress,
+ service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertNull(result.podName());
+ }
+
+ @Test
+ void podNameKindIsPod() {
+ V1EndpointAddress endpointAddress = new V1EndpointAddressBuilder()
+ .withTargetRef(new V1ObjectReferenceBuilder().withKind("Pod").withName("my-pod").build()).build();
+ V1Service service = new V1ServiceBuilder()
+ .withSpec(new V1ServiceSpecBuilder().withExternalName("external-name").build())
+ .withMetadata(new V1ObjectMeta()).build();
+
+ K8sInstanceIdHostPodNameSupplier supplier = K8sInstanceIdHostPodNameSupplier.nonExternalName(endpointAddress,
+ service);
+ InstanceIdHostPodName result = supplier.get();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(result.podName(), "my-pod");
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/K8sPodLabelsAndAnnotationsSupplierTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/K8sPodLabelsAndAnnotationsSupplierTests.java
new file mode 100644
index 0000000000..8b9ad13589
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/K8sPodLabelsAndAnnotationsSupplierTests.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import java.util.Map;
+
+import io.kubernetes.client.openapi.apis.CoreV1Api;
+import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
+import io.kubernetes.client.openapi.models.V1PodBuilder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import org.springframework.cloud.kubernetes.commons.discovery.PodLabelsAndAnnotations;
+
+/**
+ * @author wind57
+ */
+class K8sPodLabelsAndAnnotationsSupplierTests {
+
+ private static final String NAMESPACE = "spring-k8s";
+
+ private static final String POD_NAME = "my-pod";
+
+ private final CoreV1Api coreV1Api = Mockito.mock(CoreV1Api.class);
+
+ @AfterEach
+ void afterEach() {
+ Mockito.reset(coreV1Api);
+ }
+
+ @Test
+ void noObjetMeta() throws Exception {
+
+ Mockito.when(coreV1Api.readNamespacedPod(POD_NAME, NAMESPACE, null)).thenReturn(
+ new V1PodBuilder().withMetadata(new V1ObjectMetaBuilder().withName(POD_NAME).build()).build());
+
+ PodLabelsAndAnnotations result = K8sPodLabelsAndAnnotationsSupplier.nonExternalName(coreV1Api, NAMESPACE)
+ .apply(POD_NAME);
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result.labels().isEmpty());
+ Assertions.assertTrue(result.annotations().isEmpty());
+ }
+
+ @Test
+ void labelsAndAnnotationsPresent() throws Exception {
+
+ Mockito.when(coreV1Api.readNamespacedPod(POD_NAME, NAMESPACE, null))
+ .thenReturn(new V1PodBuilder().withMetadata(new V1ObjectMetaBuilder().withName(POD_NAME)
+ .withLabels(Map.of("a", "b")).withAnnotations(Map.of("c", "d")).build()).build());
+
+ PodLabelsAndAnnotations result = K8sPodLabelsAndAnnotationsSupplier.nonExternalName(coreV1Api, NAMESPACE)
+ .apply(POD_NAME);
+ Assertions.assertNotNull(result);
+ Assertions.assertEquals(result.labels(), Map.of("a", "b"));
+ Assertions.assertEquals(result.annotations(), Map.of("c", "d"));
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfigurationTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfigurationTests.java
new file mode 100644
index 0000000000..c65544cc4c
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerSelectiveNamespacesAutoConfigurationTests.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019-2023 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.discovery;
+
+import java.util.List;
+import java.util.Set;
+
+import io.kubernetes.client.openapi.ApiClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author wind57
+ */
+@ExtendWith(OutputCaptureExtension.class)
+class KubernetesClientInformerSelectiveNamespacesAutoConfigurationTests {
+
+ private static final String NAMESPACE_A = "a";
+
+ private static final String NAMESPACE_B = "b";
+
+ private static final String NAMESPACE_C = "c";
+
+ private static final String NAMESPACE_D = "d";
+
+ @Test
+ void testBeansCreates(CapturedOutput output) {
+
+ new ApplicationContextRunner()
+ .withPropertyValues("spring.cloud.discovery.enabled=true",
+ "spring.cloud.kubernetes.discovery.enabled=true",
+ "spring.cloud.kubernetes.discovery.namespaces[0]=" + NAMESPACE_A,
+ "spring.cloud.kubernetes.discovery.namespaces[1]=" + NAMESPACE_B,
+ "spring.main.cloud-platform=kubernetes")
+ .withConfiguration(
+ AutoConfigurations.of(KubernetesClientInformerSelectiveNamespacesAutoConfiguration.class))
+ .withUserConfiguration(Config.class).run(context -> {
+ assertThat(context.getBean("selectiveNamespaces")).isNotNull();
+
+ @SuppressWarnings("unchecked")
+ Set selectiveNamespaces = context.getBean("selectiveNamespaces", Set.class);
+ Assertions.assertEquals(selectiveNamespaces, Set.of("a", "b"));
+
+ @SuppressWarnings("unchecked")
+ Set namespaces = context.getBean("namespaces", Set.class);
+ Assertions.assertEquals(namespaces, Set.of("c", "d"));
+ });
+
+ assertThat(output.getOut().contains("registering lister (for services) in namespace : " + NAMESPACE_A))
+ .isTrue();
+ assertThat(output.getOut().contains("registering lister (for services) in namespace : " + NAMESPACE_B))
+ .isTrue();
+
+ assertThat(output.getOut().contains("registering lister (for services) in namespace : " + NAMESPACE_C))
+ .isFalse();
+ assertThat(output.getOut().contains("registering lister (for services) in namespace : " + NAMESPACE_D))
+ .isFalse();
+
+ assertThat(output.getOut().contains("registering lister (for endpoints) in namespace : " + NAMESPACE_A))
+ .isTrue();
+ assertThat(output.getOut().contains("registering lister (for endpoints) in namespace : " + NAMESPACE_B))
+ .isTrue();
+
+ assertThat(output.getOut().contains("registering lister (for endpoints) in namespace : " + NAMESPACE_C))
+ .isFalse();
+ assertThat(output.getOut().contains("registering lister (for endpoints) in namespace : " + NAMESPACE_D))
+ .isFalse();
+ }
+
+ @Configuration
+ @EnableConfigurationProperties(KubernetesDiscoveryProperties.class)
+ static class Config {
+
+ @Bean
+ ApiClient apiClient() {
+ return Mockito.mock(ApiClient.class);
+ }
+
+ @Bean
+ List namespaces() {
+ return List.of(NAMESPACE_C, NAMESPACE_D);
+ }
+
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests.java
index 312f396fc0..f04a3b9299 100644
--- a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests.java
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientConfigClientBootstrapConfigurationTests.java
@@ -20,6 +20,7 @@
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.JSON;
+import io.kubernetes.client.openapi.apis.CoreV1Api;
import okhttp3.OkHttpClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@@ -91,6 +92,11 @@ private void setup(String... env) {
@Configuration(proxyBeanMethods = false)
protected static class EnvironmentKnobbler {
+ @Bean
+ CoreV1Api coreV1Api(ApiClient apiClient) {
+ return new CoreV1Api(apiClient);
+ }
+
@Bean
ApiClient apiClient() {
ApiClient apiClient = mock(ApiClient.class);
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientFilterMetadataTest.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientFilterMetadataTest.java
new file mode 100644
index 0000000000..3071b3671e
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientFilterMetadataTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import io.kubernetes.client.informer.cache.Cache;
+import io.kubernetes.client.informer.cache.Lister;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPortBuilder;
+import io.kubernetes.client.openapi.models.V1Endpoints;
+import io.kubernetes.client.openapi.models.V1EndpointsBuilder;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import io.kubernetes.client.openapi.models.V1Service;
+import io.kubernetes.client.openapi.models.V1ServiceBuilder;
+import io.kubernetes.client.openapi.models.V1ServicePort;
+import io.kubernetes.client.openapi.models.V1ServicePortBuilder;
+import io.kubernetes.client.openapi.models.V1ServiceSpecBuilder;
+import org.assertj.core.util.Strings;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
+
+import static java.util.Map.entry;
+import static java.util.stream.Collectors.toList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author wind57
+ */
+class KubernetesDiscoveryClientFilterMetadataTest {
+
+ private static final SharedInformerFactoryStub STUB = new SharedInformerFactoryStub();
+
+ private static final SharedInformerStub SERVICE_SHARED_INFORMER_STUB = new SharedInformerStub<>();
+
+ private static final SharedInformerStub ENDPOINTS_SHARED_INFORMER_STUB = new SharedInformerStub<>();
+
+ private Cache servicesCache;
+
+ private Lister servicesLister;
+
+ private Cache endpointsCache;
+
+ private Lister endpointsLister;
+
+ @BeforeEach
+ void beforeEach() {
+ servicesCache = new Cache<>();
+ servicesLister = new Lister<>(servicesCache);
+
+ endpointsCache = new Cache<>();
+ endpointsLister = new Lister<>(endpointsCache);
+ }
+
+ @Test
+ void testAllExtraMetadataDisabled() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, false,
+ null, false, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "lab"), Map.of("l1", "lab"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).isEqualTo(Map.of("k8s_namespace", "ns", "type", "ClusterIP"));
+ }
+
+ @Test
+ void testLabelsEnabled() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(true, null, false,
+ null, false, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "v1", "l2", "v2"), Map.of("l1", "lab"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("l1", "v1"), entry("l2", "v2"),
+ entry("k8s_namespace", "ns"), entry("type", "ClusterIP"));
+ }
+
+ @Test
+ void testLabelsEnabledWithPrefix() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(true, "l_", false,
+ null, false, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "v1", "l2", "v2"), Map.of("l1", "lab"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("l_l1", "v1"), entry("l_l2", "v2"),
+ entry("k8s_namespace", "ns"), entry("type", "ClusterIP"));
+ }
+
+ @Test
+ void testAnnotationsEnabled() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, true,
+ null, false, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "v1"), Map.of("a1", "v1", "a2", "v2"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("a1", "v1"), entry("a2", "v2"),
+ entry("k8s_namespace", "ns"), entry("type", "ClusterIP"));
+ }
+
+ @Test
+ void testAnnotationsEnabledWithPrefix() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, true,
+ "a_", false, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "v1"), Map.of("a1", "v1", "a2", "v2"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("a_a1", "v1"), entry("a_a2", "v2"),
+ entry("k8s_namespace", "ns"), entry("type", "ClusterIP"));
+ }
+
+ @Test
+ void testPortsEnabled() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, false,
+ null, true, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "test", Map.of("l1", "v1"), Map.of("a1", "v1", "a2", "v2"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("http", "80"), entry("k8s_namespace", "test"),
+ entry("", "5555"), entry("type", "ClusterIP"));
+ }
+
+ @Test
+ void testPortsEnabledWithPrefix() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, false,
+ null, true, "p_");
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "v1"), Map.of("a1", "v1", "a2", "v2"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("p_http", "80"), entry("k8s_namespace", "ns"),
+ entry("p_", "5555"), entry("type", "ClusterIP"));
+ }
+
+ @Test
+ void testLabelsAndAnnotationsAndPortsEnabledWithPrefix() {
+ String serviceId = "s";
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(true, "l_", true,
+ "a_", true, "p_");
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ setup(serviceId, "ns", Map.of("l1", "la1"), Map.of("a1", "an1", "a2", "an2"), Map.of(80, "http", 5555, ""));
+
+ List instances = discoveryClient.getInstances(serviceId);
+ assertThat(instances).hasSize(1);
+ assertThat(instances.get(0).getMetadata()).containsOnly(entry("a_a1", "an1"), entry("a_a2", "an2"),
+ entry("l_l1", "la1"), entry("p_http", "80"), entry("k8s_namespace", "ns"), entry("type", "ClusterIP"),
+ entry("p_", "5555"));
+ }
+
+ private void setup(String serviceId, String namespace, Map labels, Map annotations,
+ Map ports) {
+
+ V1Service service = new V1ServiceBuilder()
+ .withSpec(new V1ServiceSpecBuilder().withType("ClusterIP").withPorts(getServicePorts(ports)).build())
+ .withNewMetadata().withName(serviceId).withNamespace(namespace).withLabels(labels)
+ .withAnnotations(annotations).endMetadata().build();
+
+ servicesCache.add(service);
+
+ V1ObjectMeta objectMeta = new V1ObjectMeta();
+ objectMeta.setNamespace(namespace);
+ objectMeta.setName(serviceId);
+
+ V1Endpoints endpoints = new V1EndpointsBuilder().withMetadata(objectMeta).addNewSubset()
+ .addAllToPorts(getEndpointPorts(ports)).addNewAddress().endAddress().endSubset().build();
+
+ endpointsCache.add(endpoints);
+
+ }
+
+ private List getServicePorts(Map ports) {
+ return ports.entrySet().stream().map(e -> {
+ V1ServicePortBuilder servicePortBuilder = new V1ServicePortBuilder();
+ servicePortBuilder.withPort(e.getKey());
+ if (!Strings.isNullOrEmpty(e.getValue())) {
+ servicePortBuilder.withName(e.getValue());
+ }
+ return servicePortBuilder.build();
+ }).collect(toList());
+ }
+
+ private List getEndpointPorts(Map ports) {
+ return ports.entrySet().stream().map(e -> {
+ CoreV1EndpointPortBuilder endpointPortBuilder = new CoreV1EndpointPortBuilder();
+ endpointPortBuilder.withPort(e.getKey());
+ if (!Strings.isNullOrEmpty(e.getValue())) {
+ endpointPortBuilder.withName(e.getValue());
+ }
+ return endpointPortBuilder.build();
+ }).collect(toList());
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientServiceMetadataTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientServiceMetadataTests.java
deleted file mode 100644
index 5d6b904aa1..0000000000
--- a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientServiceMetadataTests.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2013-2023 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.discovery;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import io.kubernetes.client.openapi.models.V1ObjectMeta;
-import io.kubernetes.client.openapi.models.V1Service;
-import io.kubernetes.client.openapi.models.V1ServiceSpec;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-import org.springframework.boot.test.system.CapturedOutput;
-import org.springframework.boot.test.system.OutputCaptureExtension;
-import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
-
-/**
- * @author wind57
- */
-@ExtendWith(OutputCaptureExtension.class)
-class KubernetesDiscoveryClientServiceMetadataTests {
-
- /**
- *
- * - labels are not added
- * - annotations are not added
- *
- */
- @Test
- void testServiceMetadataEmpty() {
- boolean addLabels = false;
- String labelsPrefix = "";
- boolean addAnnotations = false;
- String annotationsPrefix = "";
- boolean addPorts = false;
- String portsPrefix = "";
-
- KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(addLabels,
- labelsPrefix, addAnnotations, annotationsPrefix, addPorts, portsPrefix);
- KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
- true, "", Set.of(), Map.of(), "", metadata, 0, false, false);
- V1Service service = new V1Service().spec(new V1ServiceSpec().type("ClusterIP"))
- .metadata(new V1ObjectMeta().namespace("default"));
-
- Map result = KubernetesDiscoveryClientUtils.serviceMetadata(properties, service, "my-service");
- Assertions.assertEquals(result.size(), 2);
- Assertions.assertEquals(result, Map.of("k8s_namespace", "default", "type", "ClusterIP"));
- }
-
- /**
- *
- * - labels are added without a prefix
- * - annotations are not added
- *
- */
- @Test
- void testServiceMetadataAddLabelsNoPrefix(CapturedOutput output) {
- boolean addLabels = true;
- String labelsPrefix = "";
- boolean addAnnotations = false;
- String annotationsPrefix = "";
- boolean addPorts = false;
- String portsPrefix = "";
-
- KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(addLabels,
- labelsPrefix, addAnnotations, annotationsPrefix, addPorts, portsPrefix);
- KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
- true, "", Set.of(), Map.of(), "", metadata, 0, false, false);
- V1Service service = new V1Service().spec(new V1ServiceSpec().type("ClusterIP"))
- .metadata(new V1ObjectMeta().namespace("default").labels(Map.of("a", "b")));
-
- Map result = KubernetesDiscoveryClientUtils.serviceMetadata(properties, service, "my-service");
- Assertions.assertEquals(result.size(), 3);
- Assertions.assertEquals(result, Map.of("a", "b", "k8s_namespace", "default", "type", "ClusterIP"));
- String labelsMetadata = filterOnK8sNamespaceAndType(result);
- Assertions.assertTrue(
- output.getOut().contains("Adding labels metadata: " + labelsMetadata + " for serviceId: my-service"));
- }
-
- /**
- *
- * - labels are added with prefix
- * - annotations are not added
- *
- */
- @Test
- void testServiceMetadataAddLabelsWithPrefix(CapturedOutput output) {
- boolean addLabels = true;
- String labelsPrefix = "prefix-";
- boolean addAnnotations = false;
- String annotationsPrefix = "";
- boolean addPorts = false;
- String portsPrefix = "";
-
- KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(addLabels,
- labelsPrefix, addAnnotations, annotationsPrefix, addPorts, portsPrefix);
- KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
- true, "", Set.of(), Map.of(), "", metadata, 0, false, false);
- V1Service service = new V1Service().spec(new V1ServiceSpec().type("ClusterIP"))
- .metadata(new V1ObjectMeta().namespace("default").labels(Map.of("a", "b", "c", "d")));
-
- Map result = KubernetesDiscoveryClientUtils.serviceMetadata(properties, service, "my-service");
- Assertions.assertEquals(result.size(), 4);
- Assertions.assertEquals(result,
- Map.of("prefix-a", "b", "prefix-c", "d", "k8s_namespace", "default", "type", "ClusterIP"));
- // so that result is deterministic in assertion
- String labelsMetadata = filterOnK8sNamespaceAndType(result);
- Assertions.assertTrue(
- output.getOut().contains("Adding labels metadata: " + labelsMetadata + " for serviceId: my-service"));
- }
-
- /**
- *
- * - labels are not added
- * - annotations are added without prefix
- *
- */
- @Test
- void testServiceMetadataAddAnnotationsNoPrefix(CapturedOutput output) {
- boolean addLabels = false;
- String labelsPrefix = "";
- boolean addAnnotations = true;
- String annotationsPrefix = "";
- boolean addPorts = false;
- String portsPrefix = "";
-
- KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(addLabels,
- labelsPrefix, addAnnotations, annotationsPrefix, addPorts, portsPrefix);
- KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
- true, "", Set.of(), Map.of(), "", metadata, 0, false, false);
- V1Service service = new V1Service().spec(new V1ServiceSpec().type("ClusterIP")).metadata(
- new V1ObjectMeta().namespace("default").labels(Map.of("a", "b")).annotations(Map.of("aa", "bb")));
-
- Map result = KubernetesDiscoveryClientUtils.serviceMetadata(properties, service, "my-service");
- Assertions.assertEquals(result.size(), 3);
- Assertions.assertEquals(result, Map.of("aa", "bb", "k8s_namespace", "default", "type", "ClusterIP"));
- Assertions
- .assertTrue(output.getOut().contains("Adding annotations metadata: {aa=bb} for serviceId: my-service"));
- }
-
- /**
- *
- * - labels are not added
- * - annotations are added with prefix
- *
- */
- @Test
- void testServiceMetadataAddAnnotationsWithPrefix(CapturedOutput output) {
- boolean addLabels = false;
- String labelsPrefix = "";
- boolean addAnnotations = true;
- String annotationsPrefix = "prefix-";
- boolean addPorts = false;
- String portsPrefix = "";
-
- KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(addLabels,
- labelsPrefix, addAnnotations, annotationsPrefix, addPorts, portsPrefix);
- KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
- true, "", Set.of(), Map.of(), "", metadata, 0, false, false);
- V1Service service = new V1Service().spec(new V1ServiceSpec().type("ClusterIP")).metadata(new V1ObjectMeta()
- .namespace("default").labels(Map.of("a", "b")).annotations(Map.of("aa", "bb", "cc", "dd")));
-
- Map result = KubernetesDiscoveryClientUtils.serviceMetadata(properties, service, "my-service");
- Assertions.assertEquals(result.size(), 4);
- Assertions.assertEquals(result,
- Map.of("prefix-aa", "bb", "prefix-cc", "dd", "k8s_namespace", "default", "type", "ClusterIP"));
- // so that result is deterministic in assertion
- String annotations = filterOnK8sNamespaceAndType(result);
- Assertions.assertTrue(
- output.getOut().contains("Adding annotations metadata: " + annotations + " for serviceId: my-service"));
- }
-
- /**
- *
- * - labels are added with prefix
- * - annotations are added with prefix
- *
- */
- @Test
- void testServiceMetadataAddLabelsAndAnnotationsWithPrefix(CapturedOutput output) {
- boolean addLabels = true;
- String labelsPrefix = "label-";
- boolean addAnnotations = true;
- String annotationsPrefix = "annotation-";
- boolean addPorts = false;
- String portsPrefix = "";
-
- KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(addLabels,
- labelsPrefix, addAnnotations, annotationsPrefix, addPorts, portsPrefix);
- KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60L,
- true, "", Set.of(), Map.of(), "", metadata, 0, false, false);
- V1Service service = new V1Service().spec(new V1ServiceSpec().type("ClusterIP")).metadata(new V1ObjectMeta()
- .namespace("default").labels(Map.of("a", "b", "c", "d")).annotations(Map.of("aa", "bb", "cc", "dd")));
-
- Map result = KubernetesDiscoveryClientUtils.serviceMetadata(properties, service, "my-service");
- Assertions.assertEquals(result.size(), 6);
- Assertions.assertEquals(result, Map.of("annotation-aa", "bb", "annotation-cc", "dd", "label-a", "b", "label-c",
- "d", "k8s_namespace", "default", "type", "ClusterIP"));
- // so that result is deterministic in assertion
- String labels = result.entrySet().stream().filter(en -> en.getKey().contains("label"))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).toString();
- String annotations = result.entrySet().stream().filter(en -> en.getKey().contains("annotation"))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).toString();
- Assertions.assertTrue(
- output.getOut().contains("Adding labels metadata: " + labels + " for serviceId: my-service"));
- Assertions.assertTrue(
- output.getOut().contains("Adding annotations metadata: " + annotations + " for serviceId: my-service"));
- }
-
- private String filterOnK8sNamespaceAndType(Map result) {
- return result.entrySet().stream().filter(en -> !en.getKey().contains("k8s_namespace"))
- .filter(en -> !en.getKey().equals("type"))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).toString();
- }
-
-}
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientServiceWithoutPortNameTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientServiceWithoutPortNameTests.java
new file mode 100644
index 0000000000..5bfcc509f2
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientServiceWithoutPortNameTests.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import io.kubernetes.client.informer.cache.Cache;
+import io.kubernetes.client.informer.cache.Lister;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPortBuilder;
+import io.kubernetes.client.openapi.models.V1EndpointAddressBuilder;
+import io.kubernetes.client.openapi.models.V1EndpointSubsetBuilder;
+import io.kubernetes.client.openapi.models.V1Endpoints;
+import io.kubernetes.client.openapi.models.V1EndpointsBuilder;
+import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder;
+import io.kubernetes.client.openapi.models.V1Service;
+import io.kubernetes.client.openapi.models.V1ServiceBuilder;
+import io.kubernetes.client.openapi.models.V1ServicePortBuilder;
+import io.kubernetes.client.openapi.models.V1ServiceSpecBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
+
+/**
+ * @author wind57
+ */
+class KubernetesDiscoveryClientServiceWithoutPortNameTests {
+
+ private static final String NAMESPACE = "spring-k8s";
+
+ private static final SharedInformerFactoryStub STUB = new SharedInformerFactoryStub();
+
+ private static final SharedInformerStub SERVICE_SHARED_INFORMER_STUB = new SharedInformerStub<>();
+
+ private static final SharedInformerStub ENDPOINTS_SHARED_INFORMER_STUB = new SharedInformerStub<>();
+
+ private Cache servicesCache;
+
+ private Lister servicesLister;
+
+ private Cache endpointsCache;
+
+ private Lister endpointsLister;
+
+ @BeforeEach
+ void beforeEach() {
+ servicesCache = new Cache<>();
+ servicesLister = new Lister<>(servicesCache);
+
+ endpointsCache = new Cache<>();
+ endpointsLister = new Lister<>(endpointsCache);
+ }
+
+ @Test
+ void testDiscoveryWithoutAServicePortName() {
+
+ V1Endpoints endpoints = new V1EndpointsBuilder()
+ .withSubsets(
+ new V1EndpointSubsetBuilder().withPorts(new CoreV1EndpointPortBuilder().withPort(8080).build())
+ .withAddresses(new V1EndpointAddressBuilder().withIp("127.0.0.1").build()).build())
+ .withMetadata(
+ new V1ObjectMetaBuilder().withName("no-port-name-service").withNamespace(NAMESPACE).build())
+ .build();
+ endpointsCache.add(endpoints);
+
+ V1Service service = new V1ServiceBuilder()
+ .withSpec(
+ new V1ServiceSpecBuilder().withPorts(new V1ServicePortBuilder().withPort(8080).build()).build())
+ .withMetadata(
+ new V1ObjectMetaBuilder().withName("no-port-name-service").withNamespace(NAMESPACE).build())
+ .withSpec(new V1ServiceSpecBuilder().withType("ClusterIP").build()).build();
+ servicesCache.add(service);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(NAMESPACE),
+ true, 60, false, null, Set.of(), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0,
+ true);
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List serviceInstances = discoveryClient.getInstances("no-port-name-service");
+ Assertions.assertEquals(serviceInstances.size(), 1);
+ Assertions.assertEquals(serviceInstances.get(0).getMetadata(),
+ Map.of("port.", "8080", "k8s_namespace", "spring-k8s", "type", "ClusterIP"));
+ }
+
+}
diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientTests.java
new file mode 100644
index 0000000000..b531bf3083
--- /dev/null
+++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesDiscoveryClientTests.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright 2013-2023 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.discovery;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import io.kubernetes.client.informer.cache.Cache;
+import io.kubernetes.client.informer.cache.Lister;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPort;
+import io.kubernetes.client.openapi.models.CoreV1EndpointPortBuilder;
+import io.kubernetes.client.openapi.models.V1EndpointAddress;
+import io.kubernetes.client.openapi.models.V1EndpointAddressBuilder;
+import io.kubernetes.client.openapi.models.V1EndpointSubset;
+import io.kubernetes.client.openapi.models.V1Endpoints;
+import io.kubernetes.client.openapi.models.V1EndpointsBuilder;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import io.kubernetes.client.openapi.models.V1Service;
+import io.kubernetes.client.openapi.models.V1ServiceBuilder;
+import io.kubernetes.client.openapi.models.V1ServiceSpecBuilder;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
+import org.springframework.cloud.kubernetes.commons.discovery.KubernetesServiceInstance;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author wind57
+ */
+class KubernetesDiscoveryClientTests {
+
+ private static final SharedInformerFactoryStub STUB = new SharedInformerFactoryStub();
+
+ private static final SharedInformerStub SERVICE_SHARED_INFORMER_STUB = new SharedInformerStub<>();
+
+ private static final SharedInformerStub ENDPOINTS_SHARED_INFORMER_STUB = new SharedInformerStub<>();
+
+ private Cache servicesCache;
+
+ private Lister servicesLister;
+
+ private Cache endpointsCache;
+
+ private Lister endpointsLister;
+
+ @BeforeEach
+ void beforeEach() {
+ servicesCache = new Cache<>();
+ servicesLister = new Lister<>(servicesCache);
+
+ endpointsCache = new Cache<>();
+ endpointsLister = new Lister<>(endpointsCache);
+ }
+
+ @Test
+ void getInstancesShouldBeAbleToHandleEndpointsSingleAddress() {
+ Map labels = Map.of("l", "v");
+ String serviceId = "id";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("10");
+ List names = List.of("http");
+ List protocols = List.of("TCP");
+ List ports = List.of(80);
+ List appProtocols = List.of("appTCP");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, false,
+ null, false, null);
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("id");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("10")).hasSize(1);
+ }
+
+ @Test
+ void getInstancesShouldBeAbleToHandleEndpointsSingleAddressAndMultiplePorts() {
+ Map labels = Map.of("l2", "v2");
+ String serviceId = "endpoint";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("20");
+ List names = List.of("http", "mgmt");
+ List protocols = List.of("TCP", "TCP");
+ List ports = List.of(80, 900);
+ List appProtocols = List.of("http_tcp", "mgmt_tcp");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60,
+ false, null, Set.of(), labels, "http_tcp", KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("20")).hasSize(1).filteredOn(s -> 80 == s.getPort())
+ .hasSize(1);
+ }
+
+ @Test
+ void getInstancesShouldBeAbleToHandleEndpointsMultipleAddresses() {
+ Map labels = Map.of("l1", "v1");
+ String serviceId = "endpoint";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1", "ip2");
+ List uuids = List.of("40", "50");
+ List names = List.of("https");
+ List protocols = List.of("TCP");
+ List ports = List.of(443);
+ List appProtocols = List.of("https_tcp");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties.Metadata metadata = new KubernetesDiscoveryProperties.Metadata(false, null, false,
+ null, true, "port.");
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60,
+ false, null, Set.of(443, 8443), labels, null, metadata, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint");
+
+ assertThat(instances).hasSize(2).filteredOn(ServiceInstance::isSecure).extracting(ServiceInstance::getHost)
+ .containsOnly("ip1", "ip2");
+ }
+
+ @Test
+ void getInstancesShouldBeAbleToHandleEndpointsFromMultipleNamespaces() {
+
+ Map labels = Map.of("l", "v");
+ String serviceId = "endpoint";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("60");
+ List names = List.of("http");
+ List protocols = List.of("TCP");
+ List ports = List.of(80);
+ List appProtocols = List.of("https_tcp");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ ips = List.of("ip2");
+ uuids = List.of("70");
+ namespace = "test2";
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint");
+
+ assertThat(instances).hasSize(2);
+ assertThat(instances).filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()).hasSize(1);
+ assertThat(instances).filteredOn(s -> s.getHost().equals("ip2") && !s.isSecure()).hasSize(1);
+ assertThat(instances).filteredOn(s -> s.getServiceId().contains("endpoint")
+ && ((KubernetesServiceInstance) s).getNamespace().equals("test")).hasSize(1);
+ assertThat(instances).filteredOn(s -> s.getServiceId().contains("endpoint")
+ && ((KubernetesServiceInstance) s).getNamespace().equals("test2")).hasSize(1);
+ assertThat(instances).filteredOn(s -> s.getInstanceId().equals("60")).hasSize(1);
+ assertThat(instances).filteredOn(s -> s.getInstanceId().equals("70")).hasSize(1);
+ }
+
+ @Test
+ void instanceWithoutSubsetsShouldBeSkipped() {
+ V1Endpoints endpoints = new V1EndpointsBuilder().withNewMetadata().withName("endpoint1").withNamespace("test")
+ .withLabels(Collections.emptyMap()).endMetadata().build();
+ endpointsCache.add(endpoints);
+
+ V1Service service = new V1ServiceBuilder().withNewMetadata().withName("endpoint1").withNamespace("test").and()
+ .build();
+ servicesCache.add(service);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60,
+ false, null, Set.of(), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, true);
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint1");
+
+ assertThat(instances).isEmpty();
+ }
+
+ @Test
+ void getInstancesShouldBeAbleToHandleEndpointsSingleAddressAndMultiplePortsUsingPrimaryPortNameLabel() {
+ Map labels = Map.of("primary-port-name", "https");
+ String serviceId = "endpoint2";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("80");
+ List names = List.of("http", "https");
+ List protocols = List.of("TCP", "TCP");
+ List ports = List.of(80, 443);
+ List appProtocols = List.of("http", "https");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60,
+ false, null, Set.of(443, 8443), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0,
+ true);
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint2");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("80")).hasSize(1).filteredOn(s -> 443 == s.getPort())
+ .hasSize(1);
+ }
+
+ @Test
+ void instanceWithMultiplePortsAndMisconfiguredPrimaryPortNameInLabelWithoutFallbackShouldLogWarning() {
+ Map labels = Map.of("primary-port-name", "oops");
+ String serviceId = "endpoint3";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("90");
+ List names = List.of("httpA", "httpB", "httpC", "httpD");
+ List protocols = List.of("TCP", "TCP", "TCP", "TCP");
+ List ports = List.of(8443, 443, 80, 8080);
+ List appProtocols = List.of("https1", "https2", "http1", "http2");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(443, 8443), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0,
+ true);
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint3");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("90")).hasSize(1).hasSize(1);
+ }
+
+ @Test
+ void instanceWithMultiplePortsAndMisconfiguredGenericPrimaryPortNameWithoutFallbackShouldLogWarning() {
+ Map labels = Map.of();
+ String serviceId = "endpoint4";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("100");
+ List names = List.of("httpA", "httpB", "httpC", "httpD");
+ List protocols = List.of("TCP", "TCP", "TCP", "TCP");
+ List ports = List.of(8443, 443, 80, 8080);
+ List appProtocols = List.of("https1", "https2", "http1", "http2");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(443, 8443), Map.of(), "oops", KubernetesDiscoveryProperties.Metadata.DEFAULT, 0,
+ true);
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint4");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("100")).hasSize(1).hasSize(1);
+ }
+
+ @Test
+ void instanceWithMultiplePortsAndWithoutPrimaryPortNameSpecifiedShouldFallBackToHttps() {
+ Map labels = Map.of();
+ String serviceId = "endpoint5";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("110");
+ List names = List.of("httpA", "httpB");
+ List protocols = List.of("TCP", "TCP");
+ List ports = List.of(443, 80);
+ List appProtocols = List.of("http", "https");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, false, Set.of(), true, 60,
+ false, null, Set.of(443, 8443), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0,
+ true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint5");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("110")).hasSize(1).filteredOn(s -> 443 == s.getPort())
+ .hasSize(1);
+ }
+
+ @Test
+ void instanceWithMultiplePortsAndWithoutPrimaryPortNameSpecifiedOrHttpsPortShouldFallBackToHttp() {
+ Map labels = Map.of();
+ String serviceId = "endpoint5";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("120");
+ List names = List.of("httpA", "httpB", "httpC");
+ List protocols = List.of("http", "http", "http");
+ List ports = List.of(80, 8443, 80);
+ List appProtocols = List.of("TCP", "TCP", "TCP");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB,
+ KubernetesDiscoveryProperties.DEFAULT);
+
+ List instances = discoveryClient.getInstances("endpoint5");
+
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("120")).hasSize(1).filteredOn(s -> 80 == s.getPort())
+ .hasSize(1);
+ }
+
+ @Test
+ void instanceWithMultiplePortsAndWithoutPrimaryPortNameSpecifiedShouldLogWarning() {
+ Map labels = Map.of();
+ String serviceId = "endpoint5";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("130");
+ List names = List.of("http", "https");
+ List protocols = List.of("http", "https");
+ List ports = List.of(80, 443);
+ List appProtocols = List.of("TCP", "TCP");
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(true, true, Set.of(), true, 60,
+ true, null, Set.of(443, 8443), Map.of(), null, KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, true);
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint5");
+
+ // We're returning the first discovered port to not change previous behaviour
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getInstanceId().equals("130")).hasSize(1).filteredOn(s -> 443 == s.getPort())
+ .hasSize(1);
+ }
+
+ @Test
+ public void instanceWithoutPorts() {
+ Map labels = Map.of();
+ String serviceId = "endpoint5";
+ String serviceType = "ExternalName";
+ String namespace = "test";
+ List ips = List.of("ip1");
+ List uuids = List.of("130");
+ List names = List.of();
+ List protocols = List.of();
+ List ports = List.of();
+ List appProtocols = List.of();
+
+ setup(serviceId, serviceType, namespace, labels, ips, uuids, names, protocols, ports, appProtocols);
+
+ KubernetesDiscoveryProperties properties = KubernetesDiscoveryProperties.DEFAULT;
+
+ KubernetesInformerDiscoveryClient discoveryClient = new KubernetesInformerDiscoveryClient(STUB, servicesLister,
+ endpointsLister, SERVICE_SHARED_INFORMER_STUB, ENDPOINTS_SHARED_INFORMER_STUB, properties);
+
+ List instances = discoveryClient.getInstances("endpoint5");
+
+ // We're returning the first discovered port to not change previous behaviour
+ assertThat(instances).hasSize(1).filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()).hasSize(1)
+ .filteredOn(s -> s.getUri().toASCIIString().equals("http://ip1"))
+ .filteredOn(s -> s.getInstanceId().equals("130")).hasSize(1).filteredOn(s -> 0 == s.getPort())
+ .hasSize(1);
+ }
+
+ private void setup(String serviceId, String serviceType, String namespace, Map labels,
+ List ips, List uuids, List names, List protocols, List ports,
+ List appProtocols) {
+
+ V1Service service = new V1ServiceBuilder().withSpec(new V1ServiceSpecBuilder().withType(serviceType).build())
+ .withNewMetadata().withName(serviceId).withNamespace(namespace).withLabels(labels).endMetadata()
+ .build();
+
+ servicesCache.add(service);
+
+ V1ObjectMeta objectMeta = new V1ObjectMeta();
+ objectMeta.setNamespace(namespace);
+ objectMeta.setName(serviceId);
+
+ V1Endpoints endpoints = new V1EndpointsBuilder().withNewMetadata().withName(serviceId).withNamespace(namespace)
+ .withLabels(labels).endMetadata().build();
+
+ List addresses = new ArrayList<>();
+ for (int i = 0; i < ips.size(); ++i) {
+ V1EndpointAddress address = new V1EndpointAddressBuilder().withIp(ips.get(i)).withNewTargetRef()
+ .withUid(uuids.get(i)).endTargetRef().build();
+ addresses.add(address);
+ }
+
+ V1EndpointSubset subset = new V1EndpointSubset();
+ subset.setAddresses(addresses);
+
+ List