From 2267db7a68b02fd5f544e04fd6e8201436c9bc8a Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 13 Oct 2025 22:29:14 +0300 Subject: [PATCH 1/6] wip Signed-off-by: wind57 --- ...rnetesAbstractBlockingDiscoveryClient.java | 84 +++++++++++++++++++ ...rnetesAbstractReactiveDiscoveryClient.java | 24 ++++++ .../KubernetesCacheableDiscoveryClient.java | 51 +++++++++++ .../discovery/KubernetesDiscoveryClient.java | 57 +------------ ...coveryClientBlockingAutoConfiguration.java | 11 +++ ...rnetesDiscoveryAutoConfigurationTests.java | 56 +++++++++++++ 6 files changed, 228 insertions(+), 55 deletions(-) create mode 100644 spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java create mode 100644 spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java create mode 100644 spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java new file mode 100644 index 0000000000..e898b85c26 --- /dev/null +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013-present 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.discovery; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +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.Service; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +abstract class KubernetesAbstractBlockingDiscoveryClient implements DiscoveryClient { + + private final RestTemplate rest; + + private final boolean emptyNamespaces; + + private final Set namespaces; + + private final String discoveryServerUrl; + + KubernetesAbstractBlockingDiscoveryClient(RestTemplate rest, KubernetesDiscoveryProperties kubernetesDiscoveryProperties) { + if (!StringUtils.hasText(kubernetesDiscoveryProperties.discoveryServerUrl())) { + throw new DiscoveryServerUrlInvalidException(); + } + this.rest = rest; + this.emptyNamespaces = kubernetesDiscoveryProperties.namespaces().isEmpty(); + this.namespaces = kubernetesDiscoveryProperties.namespaces(); + this.discoveryServerUrl = kubernetesDiscoveryProperties.discoveryServerUrl(); + } + + @Override + public List getServices() { + Service[] services = rest.getForEntity(discoveryServerUrl + "/apps", Service[].class).getBody(); + if (services != null && services.length > 0) { + return Arrays.stream(services).filter(this::matchNamespaces).map(Service::name).toList(); + } + return List.of(); + } + + @Override + public List getInstances(String serviceId) { + DefaultKubernetesServiceInstance[] responseBody = rest + .getForEntity(discoveryServerUrl + "/apps/" + serviceId, DefaultKubernetesServiceInstance[].class) + .getBody(); + if (responseBody != null && responseBody.length > 0) { + return Arrays.stream(responseBody).filter(this::matchNamespaces).collect(Collectors.toList()); + } + return List.of(); + } + + @Override + public abstract String description(); + + private boolean matchNamespaces(DefaultKubernetesServiceInstance kubernetesServiceInstance) { + return emptyNamespaces || namespaces.contains(kubernetesServiceInstance.getNamespace()); + } + + private boolean matchNamespaces(Service service) { + return service.serviceInstances().isEmpty() + || service.serviceInstances().stream().anyMatch(this::matchNamespaces); + } + +} diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java new file mode 100644 index 0000000000..de092b37e4 --- /dev/null +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013-present 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.discovery; + +/** + * @author wind57 + */ +abstract class KubernetesAbstractReactiveDiscoveryClient { + +} diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java new file mode 100644 index 0000000000..1cca0ced01 --- /dev/null +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java @@ -0,0 +1,51 @@ +/* + * Copyright 2013-present 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.discovery; + +import java.util.List; + +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.web.client.RestTemplate; + +/** + * @author wind57 + */ +class KubernetesCacheableDiscoveryClient extends KubernetesAbstractBlockingDiscoveryClient { + + KubernetesCacheableDiscoveryClient(RestTemplate rest, KubernetesDiscoveryProperties kubernetesDiscoveryProperties) { + super(rest, kubernetesDiscoveryProperties); + } + + @Override + @Cacheable("k8s-blocking-discovery-services") + public List getServices() { + return super.getServices(); + } + + @Override + @Cacheable("k8s-blocking-discovery-instances") + public List getInstances(String serviceId) { + return super.getInstances(serviceId); + } + + @Override + public String description() { + return "Kubernetes Cacheable Discovery Client"; + } +} diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java index 620200f898..7e5f192f93 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java @@ -16,40 +16,16 @@ package org.springframework.cloud.kubernetes.discovery; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -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.Service; -import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; /** * @author Ryan Baxter */ -public class KubernetesDiscoveryClient implements DiscoveryClient { - - private final RestTemplate rest; - - private final boolean emptyNamespaces; - - private final Set namespaces; - - private final String discoveryServerUrl; +final class KubernetesDiscoveryClient extends KubernetesAbstractBlockingDiscoveryClient { KubernetesDiscoveryClient(RestTemplate rest, KubernetesDiscoveryProperties kubernetesDiscoveryProperties) { - if (!StringUtils.hasText(kubernetesDiscoveryProperties.discoveryServerUrl())) { - throw new DiscoveryServerUrlInvalidException(); - } - this.rest = rest; - this.emptyNamespaces = kubernetesDiscoveryProperties.namespaces().isEmpty(); - this.namespaces = kubernetesDiscoveryProperties.namespaces(); - this.discoveryServerUrl = kubernetesDiscoveryProperties.discoveryServerUrl(); + super(rest, kubernetesDiscoveryProperties); } @Override @@ -57,33 +33,4 @@ public String description() { return "Kubernetes Discovery Client"; } - @Override - public List getInstances(String serviceId) { - DefaultKubernetesServiceInstance[] responseBody = rest - .getForEntity(discoveryServerUrl + "/apps/" + serviceId, DefaultKubernetesServiceInstance[].class) - .getBody(); - if (responseBody != null && responseBody.length > 0) { - return Arrays.stream(responseBody).filter(this::matchNamespaces).collect(Collectors.toList()); - } - return List.of(); - } - - @Override - public List getServices() { - Service[] services = rest.getForEntity(discoveryServerUrl + "/apps", Service[].class).getBody(); - if (services != null && services.length > 0) { - return Arrays.stream(services).filter(this::matchNamespaces).map(Service::name).toList(); - } - return List.of(); - } - - private boolean matchNamespaces(DefaultKubernetesServiceInstance kubernetesServiceInstance) { - return emptyNamespaces || namespaces.contains(kubernetesServiceInstance.getNamespace()); - } - - private boolean matchNamespaces(Service service) { - return service.serviceInstances().isEmpty() - || service.serviceInstances().stream().anyMatch(this::matchNamespaces); - } - } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java index ed39320394..7aee778ddf 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java @@ -23,6 +23,8 @@ import org.springframework.cloud.kubernetes.commons.PodUtils; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableBlockingDisabled; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableBlockingEnabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesBlockingDiscovery; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesBlockingDiscoveryHealthInitializer; import org.springframework.context.ApplicationEventPublisher; @@ -46,11 +48,20 @@ RestTemplateBuilder restTemplateBuilder() { @Bean @ConditionalOnMissingBean + @ConditionalOnDiscoveryCacheableBlockingDisabled KubernetesDiscoveryClient kubernetesDiscoveryClient(RestTemplateBuilder restTemplateBuilder, KubernetesDiscoveryProperties properties) { return new KubernetesDiscoveryClient(restTemplateBuilder.build(), properties); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnDiscoveryCacheableBlockingEnabled + KubernetesCacheableDiscoveryClient kubernetesCacheableDiscoveryClient(RestTemplateBuilder restTemplateBuilder, + KubernetesDiscoveryProperties properties) { + return new KubernetesCacheableDiscoveryClient(restTemplateBuilder.build(), properties); + } + @Bean @ConditionalOnMissingBean PodUtils kubernetesDiscoveryPodUtils() { diff --git a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java index 06247cb911..10a4cf0867 100644 --- a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java +++ b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java @@ -266,6 +266,62 @@ void testFor1426Issue() { }); } + /** + *
+	 *     - no property related to cacheable in the blocking implementation is set, as such:
+	 *     - KubernetesDiscoveryClient is present
+	 *     - KubernetesCacheableDiscoveryClient is not present
+	 * 
+ */ + @Test + void blockingCacheableDefault() { + setupWithFilteredClassLoader(null, + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(KubernetesDiscoveryClient.class); + assertThat(context).doesNotHaveBean(KubernetesCacheableDiscoveryClient.class); + }); + } + + /** + *
+	 *     - cacheable in the blocking implementation = false, as such:
+	 *     - KubernetesDiscoveryClient is present
+	 *     - KubernetesCacheableDiscoveryClient is not present
+	 * 
+ */ + @Test + void blockingCacheableDisabled() { + setupWithFilteredClassLoader(null, + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.blocking.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(KubernetesDiscoveryClient.class); + assertThat(context).doesNotHaveBean(KubernetesCacheableDiscoveryClient.class); + }); + } + + /** + *
+	 *     - cacheable in the blocking implementation = true, as such:
+	 *     - KubernetesDiscoveryClient is not present
+	 *     - KubernetesCacheableDiscoveryClient is present
+	 * 
+ */ + @Test + void blockingCacheableEnabled() { + setupWithFilteredClassLoader(null, + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.blocking.enabled=true", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + applicationContextRunner.run(context -> { + assertThat(context).doesNotHaveBean(KubernetesDiscoveryClient.class); + assertThat(context).hasSingleBean(KubernetesCacheableDiscoveryClient.class); + }); + } + private ApplicationContextRunner applicationContextRunner; private void setupWithFilteredClassLoader(Class cls, String... properties) { From ba9a10c9f9c6230ef92c12d42e9f1afce7f56570 Mon Sep 17 00:00:00 2001 From: wind57 Date: Mon, 13 Oct 2025 22:59:59 +0300 Subject: [PATCH 2/6] wip Signed-off-by: wind57 --- ...rnetesAbstractReactiveDiscoveryClient.java | 38 ++++++++++++- ...netesCacheableReactiveDiscoveryClient.java | 52 +++++++++++++++++ ...coveryClientReactiveAutoConfiguration.java | 12 ++++ .../KubernetesReactiveDiscoveryClient.java | 30 +--------- ...rnetesDiscoveryAutoConfigurationTests.java | 56 +++++++++++++++++++ 5 files changed, 159 insertions(+), 29 deletions(-) create mode 100644 spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java index de092b37e4..aaf9b2c705 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java @@ -16,9 +16,45 @@ package org.springframework.cloud.kubernetes.discovery; +import reactor.core.publisher.Flux; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.commons.discovery.Service; +import org.springframework.util.StringUtils; +import org.springframework.web.reactive.function.client.WebClient; + /** * @author wind57 */ -abstract class KubernetesAbstractReactiveDiscoveryClient { +abstract class KubernetesAbstractReactiveDiscoveryClient implements ReactiveDiscoveryClient { + + private final WebClient webClient; + + KubernetesAbstractReactiveDiscoveryClient(WebClient.Builder webClientBuilder, KubernetesDiscoveryProperties properties) { + if (!StringUtils.hasText(properties.discoveryServerUrl())) { + throw new DiscoveryServerUrlInvalidException(); + } + webClient = webClientBuilder.baseUrl(properties.discoveryServerUrl()).build(); + } + + @Override + public Flux getServices() { + return webClient.get() + .uri("/apps") + .exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(Service.class).map(Service::name)); + } + + @Override + public Flux getInstances(String serviceId) { + return webClient.get() + .uri("/apps/" + serviceId) + .exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(DefaultKubernetesServiceInstance.class)); + } + + @Override + public abstract String description(); } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java new file mode 100644 index 0000000000..626eadb608 --- /dev/null +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java @@ -0,0 +1,52 @@ +/* + * Copyright 2013-present 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.discovery; + +import org.springframework.cache.annotation.Cacheable; +import reactor.core.publisher.Flux; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * @author wind57 + */ +class KubernetesCacheableReactiveDiscoveryClient extends KubernetesAbstractReactiveDiscoveryClient { + + KubernetesCacheableReactiveDiscoveryClient(WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties properties) { + super(webClientBuilder, properties); + } + + @Override + @Cacheable("k8s-reactive-discovery-services") + public Flux getServices() { + return super.getServices(); + } + + @Override + @Cacheable("k8s-reactive-discovery-instances") + public Flux getInstances(String serviceId) { + return super.getInstances(serviceId); + } + + @Override + public String description() { + return "Reactive Cacheable Kubernetes Discovery Client"; + } +} diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java index 4a64ba8eca..85d6ecf42a 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java @@ -23,6 +23,8 @@ import org.springframework.cloud.kubernetes.commons.PodUtils; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveDisabled; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveEnabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; import org.springframework.context.ApplicationEventPublisher; @@ -46,11 +48,21 @@ WebClient.Builder webClientBuilder() { @Bean @ConditionalOnMissingBean + @ConditionalOnDiscoveryCacheableReactiveDisabled KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient(WebClient.Builder webClientBuilder, KubernetesDiscoveryProperties properties) { return new KubernetesReactiveDiscoveryClient(webClientBuilder, properties); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnDiscoveryCacheableReactiveEnabled + KubernetesCacheableReactiveDiscoveryClient kubernetesCacheableReactiveDiscoveryClient( + WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties properties) { + return new KubernetesCacheableReactiveDiscoveryClient(webClientBuilder, properties); + } + @Bean @ConditionalOnMissingBean PodUtils kubernetesDiscoveryPodUtils() { diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesReactiveDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesReactiveDiscoveryClient.java index 2da8a10afe..5594af7a0c 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesReactiveDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesReactiveDiscoveryClient.java @@ -16,28 +16,16 @@ package org.springframework.cloud.kubernetes.discovery; -import reactor.core.publisher.Flux; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import org.springframework.cloud.kubernetes.commons.discovery.DefaultKubernetesServiceInstance; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; -import org.springframework.cloud.kubernetes.commons.discovery.Service; -import org.springframework.util.StringUtils; import org.springframework.web.reactive.function.client.WebClient; /** * @author Ryan Baxter */ -public class KubernetesReactiveDiscoveryClient implements ReactiveDiscoveryClient { - - private final WebClient webClient; +final class KubernetesReactiveDiscoveryClient extends KubernetesAbstractReactiveDiscoveryClient { KubernetesReactiveDiscoveryClient(WebClient.Builder webClientBuilder, KubernetesDiscoveryProperties properties) { - if (!StringUtils.hasText(properties.discoveryServerUrl())) { - throw new DiscoveryServerUrlInvalidException(); - } - webClient = webClientBuilder.baseUrl(properties.discoveryServerUrl()).build(); + super(webClientBuilder, properties); } @Override @@ -45,18 +33,4 @@ public String description() { return "Reactive Kubernetes Discovery Client"; } - @Override - public Flux getInstances(String serviceId) { - return webClient.get() - .uri("/apps/" + serviceId) - .exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(DefaultKubernetesServiceInstance.class)); - } - - @Override - public Flux getServices() { - return webClient.get() - .uri("/apps") - .exchangeToFlux(clientResponse -> clientResponse.bodyToFlux(Service.class).map(Service::name)); - } - } diff --git a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java index 10a4cf0867..7230dcc322 100644 --- a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java +++ b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java @@ -322,6 +322,62 @@ void blockingCacheableEnabled() { }); } + /** + *
+	 *     - no property related to cacheable in the reactive implementation is set, as such:
+	 *     - KubernetesReactiveDiscoveryClient is present
+	 *     - KubernetesCacheableReactiveDiscoveryClient is not present
+	 * 
+ */ + @Test + void reactiveCacheableDefault() { + setupWithFilteredClassLoader(null, + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(KubernetesReactiveDiscoveryClient.class); + assertThat(context).doesNotHaveBean(KubernetesCacheableReactiveDiscoveryClient.class); + }); + } + + /** + *
+	 *     - cacheable in the reactive implementation = false, as such:
+	 *     - KubernetesReactiveDiscoveryClient is present
+	 *     - KubernetesCacheableReactiveDiscoveryClient is not present
+	 * 
+ */ + @Test + void reactiveCacheableDisabled() { + setupWithFilteredClassLoader(null, + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(KubernetesReactiveDiscoveryClient.class); + assertThat(context).doesNotHaveBean(KubernetesCacheableReactiveDiscoveryClient.class); + }); + } + + /** + *
+	 *     - cacheable in the reactive implementation = true, as such:
+	 *     - KubernetesReactiveDiscoveryClient is not present
+	 *     - KubernetesCacheableReactiveDiscoveryClient is present
+	 * 
+ */ + @Test + void reactiveCacheableEnabled() { + setupWithFilteredClassLoader(null, + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=true", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + applicationContextRunner.run(context -> { + assertThat(context).doesNotHaveBean(KubernetesReactiveDiscoveryClient.class); + assertThat(context).hasSingleBean(KubernetesCacheableReactiveDiscoveryClient.class); + }); + } + private ApplicationContextRunner applicationContextRunner; private void setupWithFilteredClassLoader(Class cls, String... properties) { From 620e03b7b99a514ca6e205e6d2d8469a02bf1f12 Mon Sep 17 00:00:00 2001 From: wind57 Date: Tue, 14 Oct 2025 20:34:03 +0300 Subject: [PATCH 3/6] wip Signed-off-by: wind57 --- ...coveryClientBlockingAutoConfiguration.java | 26 ++++----- ...coveryClientReactiveAutoConfiguration.java | 56 ++++++++++++------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java index 7aee778ddf..97671f65f1 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java @@ -46,6 +46,19 @@ RestTemplateBuilder restTemplateBuilder() { return new RestTemplateBuilder(); } + @Bean + @ConditionalOnMissingBean + PodUtils kubernetesDiscoveryPodUtils() { + return new KubernetesDiscoveryPodUtils(); + } + + @Bean + @ConditionalOnSpringCloudKubernetesBlockingDiscoveryHealthInitializer + KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils podUtils, + ApplicationEventPublisher applicationEventPublisher) { + return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); + } + @Bean @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableBlockingDisabled @@ -62,17 +75,4 @@ KubernetesCacheableDiscoveryClient kubernetesCacheableDiscoveryClient(RestTempla return new KubernetesCacheableDiscoveryClient(restTemplateBuilder.build(), properties); } - @Bean - @ConditionalOnMissingBean - PodUtils kubernetesDiscoveryPodUtils() { - return new KubernetesDiscoveryPodUtils(); - } - - @Bean - @ConditionalOnSpringCloudKubernetesBlockingDiscoveryHealthInitializer - KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils podUtils, - ApplicationEventPublisher applicationEventPublisher) { - return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); - } - } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java index 85d6ecf42a..9eb712b77c 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.cloud.kubernetes.discovery; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; @@ -46,23 +47,6 @@ WebClient.Builder webClientBuilder() { return WebClient.builder(); } - @Bean - @ConditionalOnMissingBean - @ConditionalOnDiscoveryCacheableReactiveDisabled - KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient(WebClient.Builder webClientBuilder, - KubernetesDiscoveryProperties properties) { - return new KubernetesReactiveDiscoveryClient(webClientBuilder, properties); - } - - @Bean - @ConditionalOnMissingBean - @ConditionalOnDiscoveryCacheableReactiveEnabled - KubernetesCacheableReactiveDiscoveryClient kubernetesCacheableReactiveDiscoveryClient( - WebClient.Builder webClientBuilder, - KubernetesDiscoveryProperties properties) { - return new KubernetesCacheableReactiveDiscoveryClient(webClientBuilder, properties); - } - @Bean @ConditionalOnMissingBean PodUtils kubernetesDiscoveryPodUtils() { @@ -75,7 +59,7 @@ PodUtils kubernetesDiscoveryPodUtils() { @Bean @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( - ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { + ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); } @@ -83,10 +67,44 @@ KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer * unlike the blocking implementation, we need to register the health indicator. */ @Bean + @ConditionalOnBean(KubernetesReactiveDiscoveryClient.class) @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator( - KubernetesReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties) { + KubernetesReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties) { return new ReactiveDiscoveryClientHealthIndicator(client, properties); } + /** + * unlike the blocking implementation, we need to register the health indicator. + */ + @Bean + @ConditionalOnMissingBean(KubernetesReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator reactiveDiscoveryClientHealthIndicator( + WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties kubernetesDiscoveryProperties, DiscoveryClientHealthIndicatorProperties properties) { + + KubernetesReactiveDiscoveryClient reactiveClient = + new KubernetesReactiveDiscoveryClient(webClientBuilder, kubernetesDiscoveryProperties); + + return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnDiscoveryCacheableReactiveDisabled + KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient(WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties properties) { + return new KubernetesReactiveDiscoveryClient(webClientBuilder, properties); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnDiscoveryCacheableReactiveEnabled + KubernetesCacheableReactiveDiscoveryClient kubernetesCacheableReactiveDiscoveryClient( + WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties properties) { + return new KubernetesCacheableReactiveDiscoveryClient(webClientBuilder, properties); + } + } From 3a7cfac581c5eb7a06553425a743bdd2a127417c Mon Sep 17 00:00:00 2001 From: wind57 Date: Tue, 14 Oct 2025 21:43:28 +0300 Subject: [PATCH 4/6] wip Signed-off-by: wind57 --- docs/modules/ROOT/pages/discovery-client.adoc | 16 +++ ...ctiveDiscoveryClientAutoConfiguration.java | 67 +---------- ...formerReactiveHealthAutoConfiguration.java | 107 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...oConfigurationApplicationContextTests.java | 87 +++++++++----- ...oConfigurationApplicationContextTests.java | 6 +- ...rnetesAbstractBlockingDiscoveryClient.java | 5 +- ...rnetesAbstractReactiveDiscoveryClient.java | 3 +- .../KubernetesCacheableDiscoveryClient.java | 1 + ...netesCacheableReactiveDiscoveryClient.java | 5 +- ...coveryClientBlockingAutoConfiguration.java | 4 +- ...iscoveryClientHealthAutoConfiguration.java | 93 +++++++++++++++ ...coveryClientReactiveAutoConfiguration.java | 64 ++--------- ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...rnetesDiscoveryAutoConfigurationTests.java | 44 ++++--- ...DiscoveryClientAutoConfigurationTests.java | 3 +- ...ctiveDiscoveryClientAutoConfiguration.java | 54 --------- ...ctiveDiscoveryHealthAutoConfiguration.java | 107 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ...oConfigurationApplicationContextTests.java | 6 +- ...DiscoveryClientAutoConfigurationTests.java | 4 +- 21 files changed, 437 insertions(+), 242 deletions(-) create mode 100644 spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveHealthAutoConfiguration.java create mode 100644 spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientHealthAutoConfiguration.java create mode 100644 spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryHealthAutoConfiguration.java diff --git a/docs/modules/ROOT/pages/discovery-client.adoc b/docs/modules/ROOT/pages/discovery-client.adoc index 3803101fd6..8dc5b6e8e7 100644 --- a/docs/modules/ROOT/pages/discovery-client.adoc +++ b/docs/modules/ROOT/pages/discovery-client.adoc @@ -279,6 +279,22 @@ spring.cloud.kubernetes.http.discovery.catalog.watcher.enabled=true - Since http discovery has _two_ components : server and client, we strongly recommend to align versions between them, otherwise things might not work. - If you decide to disable catalog watcher, you need to disable it in both server and client. +Since version `5.0.0`, there is the possibility to cache the responses from a discovery client (we do it via the `@Cacheable` annotation). There are two properties to keep in mind here: + +[source] +---- +spring.cloud.kubernetes.discovery.cacheable.reactive.enabled +---- + +and + +[source] +---- +spring.cloud.kubernetes.discovery.cacheable.blocking.enabled +---- + +The first one enables the cacheable reactive client, and the second one, the cacheable blocking client. + By default, we use the `Endpoints`(see https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) API to find out the current state of services. There is another way though, via `EndpointSlices` (https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). Such support can be enabled via a property: `spring.cloud.kubernetes.discovery.use-endpoint-slices=true` (by default it is `false`). Of course, your cluster has to support it also. As a matter of fact, if you enable this property, but your cluster does not support it, we will fail starting the application. If you decide to enable such support, you also need proper Role/ClusterRole set-up. For example: [source] diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.java index 531bc6e16d..392919f17f 100644 --- a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.java +++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.java @@ -25,29 +25,20 @@ import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.models.V1Endpoints; import io.kubernetes.client.openapi.models.V1Service; -import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration; -import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; -import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator; import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration; -import org.springframework.cloud.kubernetes.commons.PodUtils; -import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveDisabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveEnabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; -import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.log.LogAccessor; /** * @author Ryan Baxter @@ -62,13 +53,10 @@ KubernetesClientDiscoveryClientSpelAutoConfiguration.class }) final class KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration { - private static final LogAccessor LOG = new LogAccessor( - LogFactory.getLog(KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.class)); - @Bean @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableReactiveDisabled - KubernetesClientInformerReactiveDiscoveryClient kubernetesClientReactiveDiscoveryClient( + KubernetesClientInformerReactiveDiscoveryClient kubernetesClientInformerReactiveDiscoveryClient( List sharedInformerFactories, List> serviceListers, List> endpointsListers, List> serviceInformers, List> endpointsInformers, KubernetesDiscoveryProperties properties, @@ -82,21 +70,10 @@ KubernetesClientInformerReactiveDiscoveryClient kubernetesClientReactiveDiscover return new KubernetesClientInformerReactiveDiscoveryClient(blockingClient); } - @Bean - @ConditionalOnBean(KubernetesClientInformerReactiveDiscoveryClient.class) - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator( - KubernetesClientInformerReactiveDiscoveryClient reactiveClient, - DiscoveryClientHealthIndicatorProperties properties) { - return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); - } - - // Above two beans are created when cacheable is disabled - @Bean @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableReactiveEnabled - KubernetesClientCacheableInformerReactiveDiscoveryClient kubernetesClientCacheableReactiveDiscoveryClient( + KubernetesClientCacheableInformerReactiveDiscoveryClient kubernetesClientCacheableInformerReactiveDiscoveryClient( List sharedInformerFactories, List> serviceListers, List> endpointsListers, List> serviceInformers, List> endpointsInformers, KubernetesDiscoveryProperties properties, @@ -110,44 +87,4 @@ KubernetesClientCacheableInformerReactiveDiscoveryClient kubernetesClientCacheab return new KubernetesClientCacheableInformerReactiveDiscoveryClient(blockingClient); } - @Bean - @ConditionalOnMissingBean(KubernetesClientInformerReactiveDiscoveryClient.class) - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - ReactiveDiscoveryClientHealthIndicator reactiveDiscoveryClientHealthIndicator( - List sharedInformerFactories, List> serviceListers, - List> endpointsListers, List> serviceInformers, - List> endpointsInformers, - KubernetesDiscoveryProperties kubernetesDiscoveryProperties, CoreV1Api coreV1Api, - Predicate predicate, DiscoveryClientHealthIndicatorProperties properties) { - - KubernetesClientInformerDiscoveryClient blockingClient = new KubernetesClientInformerDiscoveryClient( - sharedInformerFactories, serviceListers, endpointsListers, serviceInformers, endpointsInformers, - kubernetesDiscoveryProperties, coreV1Api, predicate); - blockingClient.afterPropertiesSet(); - - KubernetesClientInformerReactiveDiscoveryClient reactiveClient = new KubernetesClientInformerReactiveDiscoveryClient( - blockingClient); - - return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); - } - - // Above two beans are created when cacheable is enabled. In this case, we can't make - // KubernetesClientInformerDiscoveryClient a @Bean, since blocking discovery might be - // disabled and we do not want to allow wiring of it. - // Nevertheless, we still need an instance of KubernetesClientInformerDiscoveryClient - // in order to create the ReactiveDiscoveryClientHealthIndicator and - // KubernetesClientCacheableInformerReactiveDiscoveryClient. - // As such, we create two of such instances in each bean. - - /** - * Post an event so that health indicator is initialized. - */ - @Bean - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( - ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { - LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation"); - return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); - } - } diff --git a/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveHealthAutoConfiguration.java b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveHealthAutoConfiguration.java new file mode 100644 index 0000000000..b005e4fca7 --- /dev/null +++ b/spring-cloud-kubernetes-client-discovery/src/main/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveHealthAutoConfiguration.java @@ -0,0 +1,107 @@ +/* + * Copyright 2019-present 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.function.Predicate; + +import io.kubernetes.client.informer.SharedInformer; +import io.kubernetes.client.informer.SharedInformerFactory; +import io.kubernetes.client.informer.cache.Lister; +import io.kubernetes.client.openapi.apis.CoreV1Api; +import io.kubernetes.client.openapi.models.V1Endpoints; +import io.kubernetes.client.openapi.models.V1Service; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration; +import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration; +import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; +import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.PodUtils; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.log.LogAccessor; + +/** + * @author wind57 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnSpringCloudKubernetesReactiveDiscovery +@AutoConfigureBefore({ SimpleReactiveDiscoveryClientAutoConfiguration.class, + ReactiveCommonsClientAutoConfiguration.class }) +@AutoConfigureAfter({ ReactiveCompositeDiscoveryClientAutoConfiguration.class, + KubernetesDiscoveryPropertiesAutoConfiguration.class, + KubernetesClientDiscoveryClientSpelAutoConfiguration.class, + KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration.class }) +class KubernetesClientInformerReactiveHealthAutoConfiguration { + + private static final LogAccessor LOG = new LogAccessor( + LogFactory.getLog(KubernetesClientInformerReactiveHealthAutoConfiguration.class)); + + @Bean + @ConditionalOnBean(KubernetesClientInformerReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator nonCacheableReactiveDiscoveryClientHealthIndicator( + KubernetesClientInformerReactiveDiscoveryClient reactiveClient, + DiscoveryClientHealthIndicatorProperties properties) { + return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); + } + + @Bean + @ConditionalOnMissingBean(KubernetesClientInformerReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator cacheableReactiveDiscoveryClientHealthIndicator( + List sharedInformerFactories, List> serviceListers, + List> endpointsListers, List> serviceInformers, + List> endpointsInformers, + KubernetesDiscoveryProperties kubernetesDiscoveryProperties, CoreV1Api coreV1Api, + Predicate predicate, DiscoveryClientHealthIndicatorProperties properties) { + + KubernetesClientInformerDiscoveryClient blockingClient = new KubernetesClientInformerDiscoveryClient( + sharedInformerFactories, serviceListers, endpointsListers, serviceInformers, endpointsInformers, + kubernetesDiscoveryProperties, coreV1Api, predicate); + blockingClient.afterPropertiesSet(); + + KubernetesClientInformerReactiveDiscoveryClient reactiveClient = new KubernetesClientInformerReactiveDiscoveryClient( + blockingClient); + + return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); + } + + /** + * Post an event so that health indicator is initialized. + */ + @Bean + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( + ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { + LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation"); + return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); + } + +} diff --git a/spring-cloud-kubernetes-client-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-kubernetes-client-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 3527902071..c0993e185c 100644 --- a/spring-cloud-kubernetes-client-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-kubernetes-client-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -3,3 +3,4 @@ org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerDi org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerAutoConfiguration org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerReactiveDiscoveryClientAutoConfiguration org.springframework.cloud.kubernetes.client.discovery.KubernetesClientDiscoveryClientSpelAutoConfiguration +org.springframework.cloud.kubernetes.client.discovery.KubernetesClientInformerReactiveHealthAutoConfiguration diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerDiscoveryClientAutoConfigurationApplicationContextTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerDiscoveryClientAutoConfigurationApplicationContextTests.java index 9ed6969100..d9a1e7b8a9 100644 --- a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerDiscoveryClientAutoConfigurationApplicationContextTests.java +++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerDiscoveryClientAutoConfigurationApplicationContextTests.java @@ -71,8 +71,8 @@ void discoveryEnabledDefault() { assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -90,8 +90,8 @@ void discoveryEnabledDefaultWithSelectiveNamespaces() { assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -110,8 +110,8 @@ void discoveryEnabled() { assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -131,8 +131,8 @@ void discoveryEnabledWithSelectiveNamespaces() { // reactive only present assertThat(context).hasBean("reactiveIndicatorInitializer"); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); }); @@ -194,8 +194,8 @@ void kubernetesDiscoveryEnabledWithSelectiveNamespaces() { assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -245,10 +245,11 @@ void kubernetesDiscoveryBlockingEnabled() { assertThat(context).hasSingleBean(KubernetesClientInformerDiscoveryClient.class); assertThat(context).hasSingleBean(KubernetesClientInformerReactiveDiscoveryClient.class); + // one from reactive, one from ours assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -264,10 +265,11 @@ void kubernetesDiscoveryBlockingEnabledWithSelectiveNamespaces() { assertThat(context).hasSingleBean(KubernetesClientInformerDiscoveryClient.class); assertThat(context).hasSingleBean(KubernetesClientInformerReactiveDiscoveryClient.class); + // one from blocking, one from reactive assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -285,8 +287,8 @@ void kubernetesDiscoveryBlockingDisabled() { assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -304,8 +306,8 @@ void kubernetesDiscoveryBlockingDisabledWithSelectiveNamespaces() { assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -324,8 +326,8 @@ void kubernetesDiscoveryHealthIndicatorEnabled() { assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -344,8 +346,8 @@ void kubernetesDiscoveryHealthIndicatorEnabledWithSelectiveNamespaces() { assertThat(context).getBeans(KubernetesDiscoveryClientHealthIndicatorInitializer.class).hasSize(2); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -398,8 +400,8 @@ void kubernetesDiscoveryHealthIndicatorEnabledHealthIndicatorMissing() { // reactive only present assertThat(context).hasBean("reactiveIndicatorInitializer"); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -419,8 +421,8 @@ void kubernetesDiscoveryHealthIndicatorEnabledHealthIndicatorMissingWithSelectiv assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class); assertThat(context).hasBean("reactiveIndicatorInitializer"); assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); - // ours - assertThat(context).hasBean("kubernetesReactiveDiscoveryClientHealthIndicator"); + // reactive is enabled and non-cacheable is the default option + assertThat(context).hasBean("nonCacheableReactiveDiscoveryClientHealthIndicator"); // from commons, not ours assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); @@ -428,6 +430,33 @@ void kubernetesDiscoveryHealthIndicatorEnabledHealthIndicatorMissingWithSelectiv }); } + /** + *
+	 *     -
+	 * 
+ */ + @Test + void kubernetesDiscoveryReactiveCacheableEnabledBlockingDisabled() { + setup("spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.discovery.blocking.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=true", + "spring.cloud.kubernetes.discovery.namespaces=a,b"); + applicationContextRunner.run(context -> { + assertThat(context).doesNotHaveBean(KubernetesClientInformerDiscoveryClient.class); + // cacheable client is present + assertThat(context).hasSingleBean(KubernetesClientCacheableInformerReactiveDiscoveryClient.class); + + assertThat(context).hasSingleBean(KubernetesDiscoveryClientHealthIndicatorInitializer.class); + assertThat(context).getBeans(ReactiveDiscoveryClientHealthIndicator.class).hasSize(2); + // reactive is enabled and cacheable is selected + assertThat(context).hasBean("cacheableReactiveDiscoveryClientHealthIndicator"); + // from commons, not ours + assertThat(context).hasBean("simpleReactiveDiscoveryClientHealthIndicator"); + + assertInformerBeansPresent(context, 2); + }); + } + /** * reactive is disabled and should not impact blocking in any way */ @@ -527,7 +556,8 @@ private void setup(String... properties) { KubernetesClientInformerAutoConfiguration.class, KubernetesClientInformerDiscoveryClientAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, - KubernetesClientDiscoveryClientSpelAutoConfiguration.class)) + KubernetesClientDiscoveryClientSpelAutoConfiguration.class, + KubernetesClientInformerReactiveHealthAutoConfiguration.class)) .withUserConfiguration( KubernetesClientInformerDiscoveryClientAutoConfigurationApplicationContextTests.ApiClientConfig.class) .withPropertyValues(properties); @@ -541,7 +571,8 @@ private void setupWithFilteredClassLoader(Class cls, String... properties) { UtilAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, KubernetesClientInformerAutoConfiguration.class, KubernetesClientInformerDiscoveryClientAutoConfiguration.class, - KubernetesClientDiscoveryClientSpelAutoConfiguration.class)) + KubernetesClientDiscoveryClientSpelAutoConfiguration.class, + KubernetesClientInformerReactiveHealthAutoConfiguration.class)) .withUserConfiguration( KubernetesClientInformerDiscoveryClientAutoConfigurationApplicationContextTests.ApiClientConfig.class) .withClassLoader(new FilteredClassLoader(cls)) diff --git a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java index 3db1ffea71..c596c5b0d2 100644 --- a/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java +++ b/spring-cloud-kubernetes-client-discovery/src/test/java/org/springframework/cloud/kubernetes/client/discovery/KubernetesClientInformerReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java @@ -470,7 +470,8 @@ private void setup(String... properties) { KubernetesClientInformerAutoConfiguration.class, KubernetesClientInformerDiscoveryClientAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, - KubernetesClientDiscoveryClientSpelAutoConfiguration.class)) + KubernetesClientDiscoveryClientSpelAutoConfiguration.class, + KubernetesClientInformerReactiveHealthAutoConfiguration.class)) .withUserConfiguration(ApiClientConfig.class) .withPropertyValues(properties); } @@ -483,7 +484,8 @@ private void setupWithFilteredClassLoader(String name, String... properties) { UtilAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, KubernetesClientInformerAutoConfiguration.class, KubernetesClientInformerDiscoveryClientAutoConfiguration.class, - KubernetesClientDiscoveryClientSpelAutoConfiguration.class)) + KubernetesClientDiscoveryClientSpelAutoConfiguration.class, + KubernetesClientInformerReactiveHealthAutoConfiguration.class)) .withUserConfiguration(ApiClientConfig.class) .withClassLoader(new FilteredClassLoader(name)) .withPropertyValues(properties); diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java index e898b85c26..876edb4b1e 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractBlockingDiscoveryClient.java @@ -39,7 +39,8 @@ abstract class KubernetesAbstractBlockingDiscoveryClient implements DiscoveryCli private final String discoveryServerUrl; - KubernetesAbstractBlockingDiscoveryClient(RestTemplate rest, KubernetesDiscoveryProperties kubernetesDiscoveryProperties) { + KubernetesAbstractBlockingDiscoveryClient(RestTemplate rest, + KubernetesDiscoveryProperties kubernetesDiscoveryProperties) { if (!StringUtils.hasText(kubernetesDiscoveryProperties.discoveryServerUrl())) { throw new DiscoveryServerUrlInvalidException(); } @@ -78,7 +79,7 @@ private boolean matchNamespaces(DefaultKubernetesServiceInstance kubernetesServi private boolean matchNamespaces(Service service) { return service.serviceInstances().isEmpty() - || service.serviceInstances().stream().anyMatch(this::matchNamespaces); + || service.serviceInstances().stream().anyMatch(this::matchNamespaces); } } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java index aaf9b2c705..7715c36f44 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesAbstractReactiveDiscoveryClient.java @@ -33,7 +33,8 @@ abstract class KubernetesAbstractReactiveDiscoveryClient implements ReactiveDisc private final WebClient webClient; - KubernetesAbstractReactiveDiscoveryClient(WebClient.Builder webClientBuilder, KubernetesDiscoveryProperties properties) { + KubernetesAbstractReactiveDiscoveryClient(WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties properties) { if (!StringUtils.hasText(properties.discoveryServerUrl())) { throw new DiscoveryServerUrlInvalidException(); } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java index 1cca0ced01..53348567f9 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableDiscoveryClient.java @@ -48,4 +48,5 @@ public List getInstances(String serviceId) { public String description() { return "Kubernetes Cacheable Discovery Client"; } + } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java index 626eadb608..06540d22c8 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesCacheableReactiveDiscoveryClient.java @@ -16,9 +16,9 @@ package org.springframework.cloud.kubernetes.discovery; -import org.springframework.cache.annotation.Cacheable; import reactor.core.publisher.Flux; +import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.web.reactive.function.client.WebClient; @@ -29,7 +29,7 @@ class KubernetesCacheableReactiveDiscoveryClient extends KubernetesAbstractReactiveDiscoveryClient { KubernetesCacheableReactiveDiscoveryClient(WebClient.Builder webClientBuilder, - KubernetesDiscoveryProperties properties) { + KubernetesDiscoveryProperties properties) { super(webClientBuilder, properties); } @@ -49,4 +49,5 @@ public Flux getInstances(String serviceId) { public String description() { return "Reactive Cacheable Kubernetes Discovery Client"; } + } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java index 97671f65f1..aecb84738c 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientBlockingAutoConfiguration.java @@ -55,7 +55,7 @@ PodUtils kubernetesDiscoveryPodUtils() { @Bean @ConditionalOnSpringCloudKubernetesBlockingDiscoveryHealthInitializer KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils podUtils, - ApplicationEventPublisher applicationEventPublisher) { + ApplicationEventPublisher applicationEventPublisher) { return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); } @@ -71,7 +71,7 @@ KubernetesDiscoveryClient kubernetesDiscoveryClient(RestTemplateBuilder restTemp @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableBlockingEnabled KubernetesCacheableDiscoveryClient kubernetesCacheableDiscoveryClient(RestTemplateBuilder restTemplateBuilder, - KubernetesDiscoveryProperties properties) { + KubernetesDiscoveryProperties properties) { return new KubernetesCacheableDiscoveryClient(restTemplateBuilder.build(), properties); } diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientHealthAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientHealthAutoConfiguration.java new file mode 100644 index 0000000000..a9ba5345e1 --- /dev/null +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientHealthAutoConfiguration.java @@ -0,0 +1,93 @@ +/* + * Copyright 2013-present 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.discovery; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; +import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator; +import org.springframework.cloud.kubernetes.commons.PodUtils; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * @author wind57 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnSpringCloudKubernetesReactiveDiscovery +@EnableConfigurationProperties({ DiscoveryClientHealthIndicatorProperties.class, KubernetesDiscoveryProperties.class }) +@AutoConfigureAfter(KubernetesDiscoveryClientReactiveAutoConfiguration.class) +class KubernetesDiscoveryClientHealthAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + PodUtils kubernetesDiscoveryPodUtils() { + return new KubernetesDiscoveryPodUtils(); + } + + @Bean + @ConditionalOnMissingBean + WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } + + /** + * Post an event so that health indicator is initialized. + */ + @Bean + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( + ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { + return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); + } + + /** + * unlike the blocking implementation, we need to register the health indicator. + */ + @Bean + @ConditionalOnBean(KubernetesReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator( + KubernetesReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties) { + return new ReactiveDiscoveryClientHealthIndicator(client, properties); + } + + /** + * unlike the blocking implementation, we need to register the health indicator. + */ + @Bean + @ConditionalOnMissingBean(KubernetesReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator reactiveDiscoveryClientHealthIndicator(WebClient.Builder webClientBuilder, + KubernetesDiscoveryProperties kubernetesDiscoveryProperties, + DiscoveryClientHealthIndicatorProperties properties) { + + KubernetesReactiveDiscoveryClient reactiveClient = new KubernetesReactiveDiscoveryClient(webClientBuilder, + kubernetesDiscoveryProperties); + + return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); + } + +} diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java index 9eb712b77c..0fa62a8c7b 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientReactiveAutoConfiguration.java @@ -16,19 +16,13 @@ package org.springframework.cloud.kubernetes.discovery; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; -import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator; -import org.springframework.cloud.kubernetes.commons.PodUtils; -import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveDisabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveEnabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; -import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.WebClient; @@ -41,55 +35,6 @@ @EnableConfigurationProperties({ DiscoveryClientHealthIndicatorProperties.class, KubernetesDiscoveryProperties.class }) class KubernetesDiscoveryClientReactiveAutoConfiguration { - @Bean - @ConditionalOnMissingBean - WebClient.Builder webClientBuilder() { - return WebClient.builder(); - } - - @Bean - @ConditionalOnMissingBean - PodUtils kubernetesDiscoveryPodUtils() { - return new KubernetesDiscoveryPodUtils(); - } - - /** - * Post an event so that health indicator is initialized. - */ - @Bean - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( - ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { - return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); - } - - /** - * unlike the blocking implementation, we need to register the health indicator. - */ - @Bean - @ConditionalOnBean(KubernetesReactiveDiscoveryClient.class) - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator( - KubernetesReactiveDiscoveryClient client, DiscoveryClientHealthIndicatorProperties properties) { - return new ReactiveDiscoveryClientHealthIndicator(client, properties); - } - - /** - * unlike the blocking implementation, we need to register the health indicator. - */ - @Bean - @ConditionalOnMissingBean(KubernetesReactiveDiscoveryClient.class) - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - ReactiveDiscoveryClientHealthIndicator reactiveDiscoveryClientHealthIndicator( - WebClient.Builder webClientBuilder, - KubernetesDiscoveryProperties kubernetesDiscoveryProperties, DiscoveryClientHealthIndicatorProperties properties) { - - KubernetesReactiveDiscoveryClient reactiveClient = - new KubernetesReactiveDiscoveryClient(webClientBuilder, kubernetesDiscoveryProperties); - - return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); - } - @Bean @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableReactiveDisabled @@ -102,9 +47,14 @@ KubernetesReactiveDiscoveryClient kubernetesReactiveDiscoveryClient(WebClient.Bu @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableReactiveEnabled KubernetesCacheableReactiveDiscoveryClient kubernetesCacheableReactiveDiscoveryClient( - WebClient.Builder webClientBuilder, - KubernetesDiscoveryProperties properties) { + WebClient.Builder webClientBuilder, KubernetesDiscoveryProperties properties) { return new KubernetesCacheableReactiveDiscoveryClient(webClientBuilder, properties); } + @Bean + @ConditionalOnMissingBean + WebClient.Builder webClientBuilder() { + return WebClient.builder(); + } + } diff --git a/spring-cloud-kubernetes-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-kubernetes-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 5b99b7c50b..596d49329b 100644 --- a/spring-cloud-kubernetes-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-kubernetes-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,4 @@ org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientBlockingAutoConfiguration org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientReactiveAutoConfiguration org.springframework.cloud.kubernetes.discovery.KubernetesCatalogWatchAutoConfiguration +org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryClientHealthAutoConfiguration diff --git a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java index 7230dcc322..ed15589abc 100644 --- a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java +++ b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryAutoConfigurationTests.java @@ -275,9 +275,8 @@ void testFor1426Issue() { */ @Test void blockingCacheableDefault() { - setupWithFilteredClassLoader(null, - "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", - "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + setupWithFilteredClassLoader(null, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); applicationContextRunner.run(context -> { assertThat(context).hasSingleBean(KubernetesDiscoveryClient.class); assertThat(context).doesNotHaveBean(KubernetesCacheableDiscoveryClient.class); @@ -293,10 +292,9 @@ void blockingCacheableDefault() { */ @Test void blockingCacheableDisabled() { - setupWithFilteredClassLoader(null, - "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", - "spring.cloud.kubernetes.discovery.cacheable.blocking.enabled=false", - "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + setupWithFilteredClassLoader(null, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.blocking.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); applicationContextRunner.run(context -> { assertThat(context).hasSingleBean(KubernetesDiscoveryClient.class); assertThat(context).doesNotHaveBean(KubernetesCacheableDiscoveryClient.class); @@ -312,10 +310,9 @@ void blockingCacheableDisabled() { */ @Test void blockingCacheableEnabled() { - setupWithFilteredClassLoader(null, - "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", - "spring.cloud.kubernetes.discovery.cacheable.blocking.enabled=true", - "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + setupWithFilteredClassLoader(null, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.blocking.enabled=true", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); applicationContextRunner.run(context -> { assertThat(context).doesNotHaveBean(KubernetesDiscoveryClient.class); assertThat(context).hasSingleBean(KubernetesCacheableDiscoveryClient.class); @@ -331,9 +328,8 @@ void blockingCacheableEnabled() { */ @Test void reactiveCacheableDefault() { - setupWithFilteredClassLoader(null, - "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", - "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + setupWithFilteredClassLoader(null, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); applicationContextRunner.run(context -> { assertThat(context).hasSingleBean(KubernetesReactiveDiscoveryClient.class); assertThat(context).doesNotHaveBean(KubernetesCacheableReactiveDiscoveryClient.class); @@ -349,10 +345,9 @@ void reactiveCacheableDefault() { */ @Test void reactiveCacheableDisabled() { - setupWithFilteredClassLoader(null, - "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", - "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=false", - "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + setupWithFilteredClassLoader(null, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=false", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); applicationContextRunner.run(context -> { assertThat(context).hasSingleBean(KubernetesReactiveDiscoveryClient.class); assertThat(context).doesNotHaveBean(KubernetesCacheableReactiveDiscoveryClient.class); @@ -368,10 +363,9 @@ void reactiveCacheableDisabled() { */ @Test void reactiveCacheableEnabled() { - setupWithFilteredClassLoader(null, - "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", - "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=true", - "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); + setupWithFilteredClassLoader(null, "spring.main.cloud-platform=KUBERNETES", "spring.cloud.config.enabled=false", + "spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=true", + "spring.cloud.kubernetes.discovery.discovery-server-url=http://k8sdiscoveryserver"); applicationContextRunner.run(context -> { assertThat(context).doesNotHaveBean(KubernetesReactiveDiscoveryClient.class); assertThat(context).hasSingleBean(KubernetesCacheableReactiveDiscoveryClient.class); @@ -385,14 +379,16 @@ private void setupWithFilteredClassLoader(Class cls, String... properties) { if (cls != null) { applicationContextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(KubernetesDiscoveryClientBlockingAutoConfiguration.class, - KubernetesDiscoveryClientReactiveAutoConfiguration.class)) + KubernetesDiscoveryClientReactiveAutoConfiguration.class, + KubernetesDiscoveryClientHealthAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(cls)) .withPropertyValues(properties); } else { applicationContextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(KubernetesDiscoveryClientBlockingAutoConfiguration.class, - KubernetesDiscoveryClientReactiveAutoConfiguration.class)) + KubernetesDiscoveryClientReactiveAutoConfiguration.class, + KubernetesDiscoveryClientHealthAutoConfiguration.class)) .withPropertyValues(properties); } diff --git a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfigurationTests.java b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfigurationTests.java index 98cb3667de..9f4b1c04c2 100644 --- a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfigurationTests.java +++ b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfigurationTests.java @@ -35,7 +35,8 @@ class KubernetesDiscoveryClientAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(UtilAutoConfiguration.class, ReactiveCommonsClientAutoConfiguration.class, KubernetesDiscoveryClientReactiveAutoConfiguration.class, - KubernetesDiscoveryClientBlockingAutoConfiguration.class)); + KubernetesDiscoveryClientBlockingAutoConfiguration.class, + KubernetesDiscoveryClientHealthAutoConfiguration.class)); @Test void shouldWorkWithDefaults() { diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfiguration.java b/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfiguration.java index 498b952878..bf1bbf8fe7 100644 --- a/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfiguration.java @@ -20,34 +20,23 @@ import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.client.KubernetesClient; -import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.cloud.client.ConditionalOnDiscoveryHealthIndicatorEnabled; import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration; import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration; -import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; -import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator; import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration; import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; -import org.springframework.cloud.kubernetes.commons.PodUtils; -import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration; import org.springframework.cloud.kubernetes.commons.discovery.ServicePortSecureResolver; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveDisabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnDiscoveryCacheableReactiveEnabled; import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; -import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import org.springframework.core.log.LogAccessor; /** * Auto configuration for reactive discovery client. @@ -63,9 +52,6 @@ Fabric8DiscoveryClientSpelAutoConfiguration.class }) final class Fabric8ReactiveDiscoveryClientAutoConfiguration { - private static final LogAccessor LOG = new LogAccessor( - LogFactory.getLog(Fabric8ReactiveDiscoveryClientAutoConfiguration.class)); - @Bean @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableReactiveDisabled @@ -78,16 +64,6 @@ Fabric8ReactiveDiscoveryClient fabric8ReactiveDiscoveryClient(KubernetesClient c return new Fabric8ReactiveDiscoveryClient(fabric8DiscoveryClient); } - @Bean - @ConditionalOnBean(Fabric8ReactiveDiscoveryClient.class) - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - ReactiveDiscoveryClientHealthIndicator kubernetesReactiveDiscoveryClientHealthIndicator( - Fabric8ReactiveDiscoveryClient reactiveClient, DiscoveryClientHealthIndicatorProperties properties) { - return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); - } - - // Above two beans are created when cacheable is disabled - @Bean @ConditionalOnMissingBean @ConditionalOnDiscoveryCacheableReactiveEnabled @@ -100,24 +76,6 @@ Fabric8CacheableReactiveDiscoveryClient fabric8CacheableReactiveDiscoveryClient( return new Fabric8CacheableReactiveDiscoveryClient(blockingClient); } - @Bean - @ConditionalOnMissingBean(Fabric8ReactiveDiscoveryClient.class) - @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer - ReactiveDiscoveryClientHealthIndicator kubernetesReactiveClientHealthIndicator(KubernetesClient client, - KubernetesDiscoveryProperties kubernetesDiscoveryProperties, Predicate predicate, - DiscoveryClientHealthIndicatorProperties properties, Environment environment) { - - KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(environment); - ServicePortSecureResolver servicePortSecureResolver = new ServicePortSecureResolver( - kubernetesDiscoveryProperties); - Fabric8DiscoveryClient fabric8DiscoveryClient = new Fabric8DiscoveryClient(client, - kubernetesDiscoveryProperties, servicePortSecureResolver, namespaceProvider, predicate); - - Fabric8ReactiveDiscoveryClient reactiveClient = new Fabric8ReactiveDiscoveryClient(fabric8DiscoveryClient); - - return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); - } - // Above two beans are created when cacheable is enabled. In this case, we can't make // Fabric8DiscoveryClient a @Bean, since blocking discovery might be disabled and we // do @@ -127,16 +85,4 @@ ReactiveDiscoveryClientHealthIndicator kubernetesReactiveClientHealthIndicator(K // Fabric8CacheableReactiveDiscoveryClient // As such, we create two of such instances in each bean. - /** - * Post an event so that health indicator is initialized. - */ - @Bean - @ConditionalOnClass(name = "org.springframework.boot.health.contributor.ReactiveHealthIndicator") - @ConditionalOnDiscoveryHealthIndicatorEnabled - KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( - ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { - LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation"); - return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); - } - } diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryHealthAutoConfiguration.java b/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryHealthAutoConfiguration.java new file mode 100644 index 0000000000..ff473ff2c6 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-discovery/src/main/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryHealthAutoConfiguration.java @@ -0,0 +1,107 @@ +/* + * Copyright 2013-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.kubernetes.fabric8.discovery; + +import java.util.function.Predicate; + +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.KubernetesClient; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.ConditionalOnDiscoveryHealthIndicatorEnabled; +import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration; +import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration; +import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties; +import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator; +import org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; +import org.springframework.cloud.kubernetes.commons.PodUtils; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties; +import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryPropertiesAutoConfiguration; +import org.springframework.cloud.kubernetes.commons.discovery.ServicePortSecureResolver; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscovery; +import org.springframework.cloud.kubernetes.commons.discovery.conditionals.ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.log.LogAccessor; + +/** + * Auto configuration for reactive discovery client. + * + * @author wind57 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnSpringCloudKubernetesReactiveDiscovery +@AutoConfigureBefore({ SimpleReactiveDiscoveryClientAutoConfiguration.class, + ReactiveCommonsClientAutoConfiguration.class }) +@AutoConfigureAfter({ ReactiveCompositeDiscoveryClientAutoConfiguration.class, + Fabric8DiscoveryClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class, + Fabric8DiscoveryClientSpelAutoConfiguration.class, Fabric8ReactiveDiscoveryClientAutoConfiguration.class }) +final class Fabric8ReactiveDiscoveryHealthAutoConfiguration { + + private static final LogAccessor LOG = new LogAccessor( + LogFactory.getLog(Fabric8ReactiveDiscoveryHealthAutoConfiguration.class)); + + // when cacheable is disabled + @Bean + @ConditionalOnBean(Fabric8ReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator nonCacheableReactiveDiscoveryClientHealthIndicator( + Fabric8ReactiveDiscoveryClient reactiveClient, DiscoveryClientHealthIndicatorProperties properties) { + return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); + } + + // when cacheable is enabled + @Bean + @ConditionalOnMissingBean(Fabric8ReactiveDiscoveryClient.class) + @ConditionalOnSpringCloudKubernetesReactiveDiscoveryHealthInitializer + ReactiveDiscoveryClientHealthIndicator cacheableReactiveDiscoveryClientHealthIndicator(KubernetesClient client, + KubernetesDiscoveryProperties kubernetesDiscoveryProperties, Predicate predicate, + DiscoveryClientHealthIndicatorProperties properties, Environment environment) { + + KubernetesNamespaceProvider namespaceProvider = new KubernetesNamespaceProvider(environment); + ServicePortSecureResolver servicePortSecureResolver = new ServicePortSecureResolver( + kubernetesDiscoveryProperties); + Fabric8DiscoveryClient fabric8DiscoveryClient = new Fabric8DiscoveryClient(client, + kubernetesDiscoveryProperties, servicePortSecureResolver, namespaceProvider, predicate); + + Fabric8ReactiveDiscoveryClient reactiveClient = new Fabric8ReactiveDiscoveryClient(fabric8DiscoveryClient); + + return new ReactiveDiscoveryClientHealthIndicator(reactiveClient, properties); + } + + /** + * Post an event so that health indicator is initialized. + */ + @Bean + @ConditionalOnClass(name = "org.springframework.boot.health.contributor.ReactiveHealthIndicator") + @ConditionalOnDiscoveryHealthIndicatorEnabled + KubernetesDiscoveryClientHealthIndicatorInitializer reactiveIndicatorInitializer( + ApplicationEventPublisher applicationEventPublisher, PodUtils podUtils) { + LOG.debug(() -> "Will publish InstanceRegisteredEvent from reactive implementation"); + return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, applicationEventPublisher); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 8cba896bfb..3fb9dedbcf 100644 --- a/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -2,3 +2,4 @@ org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8CatalogWatchAutoCo org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientAutoConfiguration org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8ReactiveDiscoveryClientAutoConfiguration org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientSpelAutoConfiguration +org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8ReactiveDiscoveryClientAutoConfiguration diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java index 85544a11ff..e71346adfe 100644 --- a/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java +++ b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationApplicationContextTests.java @@ -219,7 +219,8 @@ private void setup(String... properties) { ReactiveCommonsClientAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, Fabric8AutoConfiguration.class, Fabric8ReactiveDiscoveryClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class, - Fabric8DiscoveryClientSpelAutoConfiguration.class)) + Fabric8DiscoveryClientSpelAutoConfiguration.class, + Fabric8ReactiveDiscoveryHealthAutoConfiguration.class)) .withPropertyValues(properties); } @@ -229,7 +230,8 @@ private void setupWithFilteredClassLoader(String name, String... properties) { ReactiveCommonsClientAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, Fabric8AutoConfiguration.class, Fabric8ReactiveDiscoveryClientAutoConfiguration.class, KubernetesDiscoveryPropertiesAutoConfiguration.class, - Fabric8DiscoveryClientSpelAutoConfiguration.class)) + Fabric8DiscoveryClientSpelAutoConfiguration.class, + Fabric8ReactiveDiscoveryHealthAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(name)) .withPropertyValues(properties); } diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationTests.java b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationTests.java index f44da7b311..0680c0bce5 100644 --- a/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationTests.java +++ b/spring-cloud-kubernetes-fabric8-discovery/src/test/java/org/springframework/cloud/kubernetes/fabric8/discovery/Fabric8ReactiveDiscoveryClientAutoConfigurationTests.java @@ -41,8 +41,8 @@ class Fabric8ReactiveDiscoveryClientAutoConfigurationTests { ReactiveCommonsClientAutoConfiguration.class, KubernetesCommonsAutoConfiguration.class, Fabric8AutoConfiguration.class, Fabric8DiscoveryClientAutoConfiguration.class, Fabric8ReactiveDiscoveryClientAutoConfiguration.class, - KubernetesDiscoveryPropertiesAutoConfiguration.class, - Fabric8DiscoveryClientSpelAutoConfiguration.class)); + KubernetesDiscoveryPropertiesAutoConfiguration.class, Fabric8DiscoveryClientSpelAutoConfiguration.class, + Fabric8ReactiveDiscoveryHealthAutoConfiguration.class)); @Test void shouldWorkWithDefaults() { From 95236420c6a13c70101d203682d801e87b83c003 Mon Sep 17 00:00:00 2001 From: wind57 Date: Tue, 14 Oct 2025 21:56:14 +0300 Subject: [PATCH 5/6] wip Signed-off-by: wind57 --- docs/modules/ROOT/pages/discovery-client.adoc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/discovery-client.adoc b/docs/modules/ROOT/pages/discovery-client.adoc index 8dc5b6e8e7..a6d8346490 100644 --- a/docs/modules/ROOT/pages/discovery-client.adoc +++ b/docs/modules/ROOT/pages/discovery-client.adoc @@ -293,7 +293,15 @@ and spring.cloud.kubernetes.discovery.cacheable.blocking.enabled ---- -The first one enables the cacheable reactive client, and the second one, the cacheable blocking client. +The first one enables the cacheable reactive client, and the second one, the cacheable blocking client. By default, the non-cacheable discovery clients are created; if you want the cacheable one, you need to toggle one of the above properties. For example: + +[source] +---- +spring.cloud.kubernetes.discovery.cacheable.reactive.enabled=true +---- + +will provide you the cacheable reactive discovery client. + By default, we use the `Endpoints`(see https://kubernetes.io/docs/concepts/services-networking/service/#endpoints) API to find out the current state of services. There is another way though, via `EndpointSlices` (https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/). Such support can be enabled via a property: `spring.cloud.kubernetes.discovery.use-endpoint-slices=true` (by default it is `false`). Of course, your cluster has to support it also. As a matter of fact, if you enable this property, but your cluster does not support it, we will fail starting the application. If you decide to enable such support, you also need proper Role/ClusterRole set-up. For example: From 44a995c002ec2fc6bb65b55262b81492de8e3d6d Mon Sep 17 00:00:00 2001 From: wind57 Date: Tue, 14 Oct 2025 22:34:07 +0300 Subject: [PATCH 6/6] fix test Signed-off-by: wind57 --- ....springframework.boot.autoconfigure.AutoConfiguration.imports | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 3fb9dedbcf..ca3dd8dce2 100644 --- a/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-kubernetes-fabric8-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -3,3 +3,4 @@ org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientAut org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8ReactiveDiscoveryClientAutoConfiguration org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8DiscoveryClientSpelAutoConfiguration org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8ReactiveDiscoveryClientAutoConfiguration +org.springframework.cloud.kubernetes.fabric8.discovery.Fabric8ReactiveDiscoveryHealthAutoConfiguration