diff --git a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java index 15370170..9c60efce 100644 --- a/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java +++ b/spring-cloud-kubernetes-client-autoconfig/src/main/java/org/springframework/cloud/kubernetes/client/KubernetesClientPodUtils.java @@ -75,7 +75,7 @@ public class KubernetesClientPodUtils implements PodUtils { } @Override - public Boolean isInsideKubernetes() { + public boolean isInsideKubernetes() { return currentPod().get() != null; } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/PodUtils.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/PodUtils.java index d4ab4bc1..5a4a452d 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/PodUtils.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/PodUtils.java @@ -26,14 +26,14 @@ import java.util.function.Supplier; public interface PodUtils { /** - * @return A supplier of the currentPod {@link Pod}. The supplier will hold the - * currentPod {@link Pod} if inside Kubernetes or false, otherwise. + * @return A supplier of the current Pod. The supplier will hold the current Pod if + * inside Kubernetes or null, otherwise. */ Supplier currentPod(); /** * @return true if called from within Kubernetes, false otherwise. */ - Boolean isInsideKubernetes(); + boolean isInsideKubernetes(); } diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/MultipleSourcesContainer.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/MultipleSourcesContainer.java index f8a39c91..892171f7 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/MultipleSourcesContainer.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/MultipleSourcesContainer.java @@ -25,7 +25,7 @@ import java.util.Map; * Container that stores multiple sources, to be exact their names and their flattenned * data. We force a LinkedHashSet on purpose, to preserve the order of sources. */ -public final record MultipleSourcesContainer(LinkedHashSet names, Map data) { +public record MultipleSourcesContainer(LinkedHashSet names, Map data) { private static final MultipleSourcesContainer EMPTY = new MultipleSourcesContainer(new LinkedHashSet<>(0), Map.of()); diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializer.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializer.java index 939ec97d..154c017f 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializer.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializer.java @@ -16,15 +16,21 @@ package org.springframework.cloud.kubernetes.commons.discovery; -import org.springframework.beans.factory.InitializingBean; +import jakarta.annotation.PostConstruct; +import org.apache.commons.logging.LogFactory; + import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; import org.springframework.cloud.kubernetes.commons.PodUtils; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.log.LogAccessor; /** * @author Ryan Baxter */ -public final class KubernetesDiscoveryClientHealthIndicatorInitializer implements InitializingBean { +public final class KubernetesDiscoveryClientHealthIndicatorInitializer { + + private static final LogAccessor LOG = new LogAccessor( + LogFactory.getLog(KubernetesDiscoveryClientHealthIndicatorInitializer.class)); private final PodUtils podUtils; @@ -36,9 +42,21 @@ public final class KubernetesDiscoveryClientHealthIndicatorInitializer implement this.applicationEventPublisher = applicationEventPublisher; } - @Override - public void afterPropertiesSet() { - this.applicationEventPublisher.publishEvent(new InstanceRegisteredEvent<>(podUtils.currentPod(), null)); + @PostConstruct + private void postConstruct() { + LOG.debug(() -> "publishing InstanceRegisteredEvent"); + this.applicationEventPublisher.publishEvent(new InstanceRegisteredEvent<>( + new RegisteredEventSource("kubernetes", podUtils.isInsideKubernetes(), podUtils.currentPod().get()), + null)); + } + + /** + * @param cloudPlatform "kubernetes" always + * @param inside inside kubernetes or not + * @param pod an actual pod or null, if we are outside kubernetes + */ + public record RegisteredEventSource(String cloudPlatform, boolean inside, Object pod) { + } } diff --git a/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializerTests.java b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializerTests.java new file mode 100644 index 00000000..ab7dd829 --- /dev/null +++ b/spring-cloud-kubernetes-commons/src/test/java/org/springframework/cloud/kubernetes/commons/discovery/KubernetesDiscoveryClientHealthIndicatorInitializerTests.java @@ -0,0 +1,146 @@ +/* + * Copyright 2013-2022 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.commons.discovery; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; +import org.springframework.cloud.kubernetes.commons.PodUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource; + +/** + * @author wind57 + * + * Tests the + * {@link org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryClientHealthIndicatorInitializer} + * with the fabric8 client. + * + */ +class KubernetesDiscoveryClientHealthIndicatorInitializerTests { + + private static ApplicationEventPublisher publisher; + + // we don't really need an actual Pod here (fabric8 or k8s-native), but only + // "something" we can assert for. + private static final Object POD = Mockito.mock(Object.class); + + @AfterEach + void afterEach() { + Mockito.reset(publisher, POD); + } + + @Test + @SuppressWarnings("unchecked") + void testInstanceRegistrationEventPublishedWhenInsideK8s() { + new ApplicationContextRunner() + .withUserConfiguration(InstanceRegistrationEventPublishedInsideK8sConfiguration.class) + .run(context -> assertThat(context).hasSingleBean(PodUtils.class)); + + ArgumentCaptor> captor = ArgumentCaptor + .forClass(InstanceRegisteredEvent.class); + Mockito.verify(publisher, Mockito.times(1)).publishEvent(captor.capture()); + KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource source = (KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource) captor + .getValue().getSource(); + assertThat(source.cloudPlatform()).isEqualTo("kubernetes"); + assertThat(source.inside()).isTrue(); + assertThat(source.pod()).isSameAs(POD); + + } + + @Test + @SuppressWarnings("unchecked") + void testInstanceRegistrationEventPublishedWhenOutsideK8s() { + new ApplicationContextRunner() + .withUserConfiguration(InstanceRegistrationEventPublishedOutsideK8sConfiguration.class) + .run(context -> assertThat(context).hasSingleBean(PodUtils.class)); + + ArgumentCaptor> captor = ArgumentCaptor + .forClass(InstanceRegisteredEvent.class); + Mockito.verify(publisher, Mockito.times(1)).publishEvent(captor.capture()); + KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource source = (KubernetesDiscoveryClientHealthIndicatorInitializer.RegisteredEventSource) captor + .getValue().getSource(); + assertThat(source.cloudPlatform()).isEqualTo("kubernetes"); + assertThat(source.inside()).isFalse(); + assertThat(source.pod()).isNotNull(); + + } + + @Configuration + static class InstanceRegistrationEventPublishedInsideK8sConfiguration { + + @Bean + @SuppressWarnings("unchecked") + PodUtils podUtils() { + PodUtils podUtils = Mockito.mock(PodUtils.class); + Mockito.when(podUtils.isInsideKubernetes()).thenReturn(true); + Mockito.when(podUtils.currentPod()).thenReturn(() -> POD); + return podUtils; + } + + @Bean + @Primary + ApplicationEventPublisher publisher() { + publisher = Mockito.mock(ApplicationEventPublisher.class); + return publisher; + } + + @Bean + KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils podUtils, + ApplicationEventPublisher publisher) { + return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, publisher); + } + + } + + @Configuration + static class InstanceRegistrationEventPublishedOutsideK8sConfiguration { + + @Bean + @SuppressWarnings("unchecked") + PodUtils podUtils() { + PodUtils podUtils = Mockito.mock(PodUtils.class); + Mockito.when(podUtils.isInsideKubernetes()).thenReturn(false); + Mockito.when(podUtils.currentPod()).thenReturn(() -> POD); + return podUtils; + } + + @Bean + @Primary + ApplicationEventPublisher publisher() { + publisher = Mockito.mock(ApplicationEventPublisher.class); + return publisher; + } + + @Bean + KubernetesDiscoveryClientHealthIndicatorInitializer indicatorInitializer(PodUtils podUtils, + ApplicationEventPublisher publisher) { + return new KubernetesDiscoveryClientHealthIndicatorInitializer(podUtils, publisher); + } + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8PodUtils.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8PodUtils.java index 4f888234..e7066f9e 100644 --- a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8PodUtils.java +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8PodUtils.java @@ -74,7 +74,7 @@ public class Fabric8PodUtils implements PodUtils { } @Override - public Boolean isInsideKubernetes() { + public boolean isInsideKubernetes() { return currentPod().get() != null; }