diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/client/loadbalancer/KubernetesClientServicesListSupplier.java b/spring-cloud-kubernetes-client-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/client/loadbalancer/KubernetesClientServicesListSupplier.java index 6b13b79e1..af2aca8d0 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/client/loadbalancer/KubernetesClientServicesListSupplier.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/client/loadbalancer/KubernetesClientServicesListSupplier.java @@ -57,32 +57,34 @@ public KubernetesClientServicesListSupplier(Environment environment, @Override public Flux> get() { - List result = new ArrayList<>(); - String serviceName = getServiceId(); - LOG.debug(() -> "serviceID : " + serviceName); - - if (discoveryProperties.allNamespaces()) { - LOG.debug(() -> "discovering services in all namespaces"); - List services = services(null, serviceName); - services.forEach(service -> addMappedService(mapper, result, service)); - } - else if (!discoveryProperties.namespaces().isEmpty()) { - List selectiveNamespaces = discoveryProperties.namespaces().stream().sorted().toList(); - LOG.debug(() -> "discovering services in selective namespaces : " + selectiveNamespaces); - selectiveNamespaces.forEach(selectiveNamespace -> { - List services = services(selectiveNamespace, serviceName); + return Flux.defer(() -> { + List result = new ArrayList<>(); + String serviceName = getServiceId(); + LOG.debug(() -> "serviceID : " + serviceName); + + if (discoveryProperties.allNamespaces()) { + LOG.debug(() -> "discovering services in all namespaces"); + List services = services(null, serviceName); services.forEach(service -> addMappedService(mapper, result, service)); - }); - } - else { - String namespace = getApplicationNamespace(null, "loadbalancer-service", kubernetesNamespaceProvider); - LOG.debug(() -> "discovering services in namespace : " + namespace); - List services = services(namespace, serviceName); - services.forEach(service -> addMappedService(mapper, result, service)); - } + } + else if (!discoveryProperties.namespaces().isEmpty()) { + List selectiveNamespaces = discoveryProperties.namespaces().stream().sorted().toList(); + LOG.debug(() -> "discovering services in selective namespaces : " + selectiveNamespaces); + selectiveNamespaces.forEach(selectiveNamespace -> { + List services = services(selectiveNamespace, serviceName); + services.forEach(service -> addMappedService(mapper, result, service)); + }); + } + else { + String namespace = getApplicationNamespace(null, "loadbalancer-service", kubernetesNamespaceProvider); + LOG.debug(() -> "discovering services in namespace : " + namespace); + List services = services(namespace, serviceName); + services.forEach(service -> addMappedService(mapper, result, service)); + } - LOG.debug(() -> "found services : " + result); - return Flux.just(result); + LOG.debug(() -> "found services : " + result); + return Flux.just(result); + }); } private void addMappedService(KubernetesServiceInstanceMapper mapper, List services, diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/App.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/App.java new file mode 100644 index 000000000..e4ddd5930 --- /dev/null +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/App.java @@ -0,0 +1,29 @@ +/* + * 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.client.loadbalancer.it; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + + public static void main(String[] args) { + SpringApplication.run(App.class); + } + +} diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerConfiguration.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerConfiguration.java new file mode 100644 index 000000000..443972df8 --- /dev/null +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/LoadBalancerConfiguration.java @@ -0,0 +1,33 @@ +/* + * 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.client.loadbalancer.it; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.client.WebClient; + +@TestConfiguration +public class LoadBalancerConfiguration { + + @Bean + @LoadBalanced + WebClient.Builder client() { + return WebClient.builder(); + } + +} diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/Util.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/Util.java index 609b92c93..fa36747b3 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/Util.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/Util.java @@ -25,20 +25,16 @@ import io.kubernetes.client.openapi.models.V1Endpoints; import io.kubernetes.client.openapi.models.V1EndpointsBuilder; import io.kubernetes.client.openapi.models.V1EndpointsList; +import io.kubernetes.client.openapi.models.V1EndpointsListBuilder; +import io.kubernetes.client.openapi.models.V1ListMetaBuilder; import io.kubernetes.client.openapi.models.V1ObjectMetaBuilder; import io.kubernetes.client.openapi.models.V1Service; import io.kubernetes.client.openapi.models.V1ServiceBuilder; import io.kubernetes.client.openapi.models.V1ServiceList; +import io.kubernetes.client.openapi.models.V1ServiceListBuilder; import io.kubernetes.client.openapi.models.V1ServicePortBuilder; import io.kubernetes.client.openapi.models.V1ServiceSpecBuilder; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.function.client.WebClient; - /** * @author wind57 */ @@ -142,24 +138,26 @@ public static void endpointsInNamespaceServiceMode(WireMockServer server, V1Endp .willReturn(WireMock.aResponse().withBody(new JSON().serialize(endpointsList)).withStatus(200))); } - @TestConfiguration - public static class LoadBalancerConfiguration { + public static void mockWatchers(WireMockServer wireMockServer) { + V1Service serviceA = Util.service("a", "service-a", 8888); - @Bean - @LoadBalanced - WebClient.Builder client() { - return WebClient.builder(); - } + V1ServiceList serviceListA = new V1ServiceListBuilder() + .withNewMetadataLike(new V1ListMetaBuilder().withResourceVersion("0").build()) + .endMetadata() + .withItems(serviceA) + .build(); - } + servicesInNamespaceServiceMode(wireMockServer, serviceListA, "a", "service-a"); - @SpringBootApplication - public static class Configuration { + V1Endpoints endpointsA = Util.endpoints("a", "service-a", 8888, "127.0.0.1"); - public static void main(String[] args) { - SpringApplication.run(Configuration.class); - } + V1EndpointsList endpointsListA = new V1EndpointsListBuilder() + .withNewMetadataLike(new V1ListMetaBuilder().withResourceVersion("0").build()) + .endMetadata() + .withItems(endpointsA) + .build(); + Util.endpointsInNamespaceServiceMode(wireMockServer, endpointsListA, "a", "service-a"); } } diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheDisabledTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheDisabledTest.java new file mode 100644 index 000000000..f6f85a47c --- /dev/null +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheDisabledTest.java @@ -0,0 +1,126 @@ +/* + * 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.client.loadbalancer.it.mode.cache; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.test.annotation.DirtiesContext; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; + +/** + * @author wind57 + */ +@SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", + "spring.cloud.kubernetes.client.namespace=a", "spring.cloud.loadbalancer.cache.enabled=false" }, + classes = App.class) +@DirtiesContext +class CacheDisabledTest { + + private static final int SERVICE_PORT = 8888; + + private static WireMockServer wireMockServer; + + private static WireMockServer serviceAMockServer; + + private static MockedStatic clientUtils; + + @SuppressWarnings("rawtypes") + private static final MockedStatic MOCKED_STATIC = Mockito + .mockStatic(KubernetesServiceInstanceMapper.class); + + @Autowired + private LoadBalancerClientFactory loadBalancerClientFactory; + + @BeforeAll + static void beforeAll() { + + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + Util.mockWatchers(wireMockServer); + + serviceAMockServer = new WireMockServer(SERVICE_PORT); + serviceAMockServer.start(); + WireMock.configureFor("localhost", SERVICE_PORT); + + // we mock host creation so that it becomes something like : localhost:8888 + // then wiremock can catch this request, and we can assert for the result + MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local")) + .thenReturn("localhost"); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + // we need to not mock 'getApplicationNamespace' + clientUtils = mockStatic(KubernetesClientUtils.class, Mockito.CALLS_REAL_METHODS); + clientUtils.when(KubernetesClientUtils::kubernetesApiClient).thenReturn(client); + } + + @AfterAll + static void afterAll() { + wireMockServer.stop(); + serviceAMockServer.stop(); + MOCKED_STATIC.close(); + clientUtils.close(); + } + + /** + *
+	 *      - we disable caching via 'spring.cloud.loadbalancer.cache.enabled=false'
+	 *      - as such, two calls to : loadBalancer.choose() will both execute
+	 *        on the delegate itself, which we assert via 'wireMockServer.verify'
+	 * 
+ */ + @Test + void test() { + + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance("service-a"); + Response firstResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(firstResponse.hasServer()).isTrue(); + + Response secondResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(secondResponse.hasServer()).isTrue(); + + // called two times + wireMockServer.verify(WireMock.exactly(2), WireMock.getRequestedFor( + WireMock.urlEqualTo("/api/v1/namespaces/a/services?fieldSelector=metadata.name%3D" + "service-a"))); + + } + +} diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheEnabledOutsideTTLTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheEnabledOutsideTTLTest.java new file mode 100644 index 000000000..8e0884f06 --- /dev/null +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheEnabledOutsideTTLTest.java @@ -0,0 +1,130 @@ +/* + * 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.client.loadbalancer.it.mode.cache; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.test.annotation.DirtiesContext; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; + +/** + * @author wind57 + */ +@SpringBootTest( + properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a", + "spring.cloud.loadbalancer.cache.enabled=true", "spring.cloud.loadbalancer.cache.ttl=2s" }, + classes = App.class) +@DirtiesContext +class CacheEnabledOutsideTTLTest { + + private static final int SERVICE_PORT = 8888; + + private static WireMockServer wireMockServer; + + private static WireMockServer serviceAMockServer; + + private static MockedStatic clientUtils; + + @SuppressWarnings("rawtypes") + private static final MockedStatic MOCKED_STATIC = Mockito + .mockStatic(KubernetesServiceInstanceMapper.class); + + @Autowired + private LoadBalancerClientFactory loadBalancerClientFactory; + + @BeforeAll + static void beforeAll() { + + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + Util.mockWatchers(wireMockServer); + + serviceAMockServer = new WireMockServer(SERVICE_PORT); + serviceAMockServer.start(); + WireMock.configureFor("localhost", SERVICE_PORT); + + // we mock host creation so that it becomes something like : localhost:8888 + // then wiremock can catch this request, and we can assert for the result + MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local")) + .thenReturn("localhost"); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + // we need to not mock 'getApplicationNamespace' + clientUtils = mockStatic(KubernetesClientUtils.class, Mockito.CALLS_REAL_METHODS); + clientUtils.when(KubernetesClientUtils::kubernetesApiClient).thenReturn(client); + } + + @AfterAll + static void afterAll() { + wireMockServer.stop(); + serviceAMockServer.stop(); + MOCKED_STATIC.close(); + clientUtils.close(); + } + + /** + *
+	 *      - caching is enabled and : 'spring.cloud.loadbalancer.cache.ttl=2s'
+	 *      - we make one call now, and one after 2s.
+	 *      - as such, first loadBalancer.choose() will execute on the delegate,
+	 *        it will be cached. And the second call, since it got TTL-ed, will happen
+	 *        on the delegate again.
+	 *
+	 * 
+ */ + @Test + void test() throws Exception { + + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance("service-a"); + Mono.from(loadBalancer.choose()).block(); + // ttl will expire the first flux + Thread.sleep(2500); + Response response = Mono.from(loadBalancer.choose()).block(); + assertThat(response.hasServer()).as("there should be one server").isTrue(); + + // called two times + wireMockServer.verify(WireMock.exactly(2), WireMock.getRequestedFor( + WireMock.urlEqualTo("/api/v1/namespaces/a/services?fieldSelector=metadata.name%3D" + "service-a"))); + + } + +} diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheEnabledWithinTTLTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheEnabledWithinTTLTest.java new file mode 100644 index 000000000..df0c565e2 --- /dev/null +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/cache/CacheEnabledWithinTTLTest.java @@ -0,0 +1,128 @@ +/* + * 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.client.loadbalancer.it.mode.cache; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.kubernetes.client.openapi.ApiClient; +import io.kubernetes.client.util.ClientBuilder; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.test.annotation.DirtiesContext; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mockStatic; + +/** + * @author wind57 + */ +@SpringBootTest( + properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a", + "spring.cloud.loadbalancer.cache.enabled=true", "spring.cloud.loadbalancer.cache.ttl=2s" }, + classes = App.class) +@DirtiesContext +class CacheEnabledWithinTTLTest { + + private static final int SERVICE_PORT = 8888; + + private static WireMockServer wireMockServer; + + private static WireMockServer serviceAMockServer; + + private static MockedStatic clientUtils; + + @SuppressWarnings("rawtypes") + private static final MockedStatic MOCKED_STATIC = Mockito + .mockStatic(KubernetesServiceInstanceMapper.class); + + @Autowired + private LoadBalancerClientFactory loadBalancerClientFactory; + + @BeforeAll + static void beforeAll() { + + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + Util.mockWatchers(wireMockServer); + + serviceAMockServer = new WireMockServer(SERVICE_PORT); + serviceAMockServer.start(); + WireMock.configureFor("localhost", SERVICE_PORT); + + // we mock host creation so that it becomes something like : localhost:8888 + // then wiremock can catch this request, and we can assert for the result + MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local")) + .thenReturn("localhost"); + + ApiClient client = new ClientBuilder().setBasePath("http://localhost:" + wireMockServer.port()).build(); + // we need to not mock 'getApplicationNamespace' + clientUtils = mockStatic(KubernetesClientUtils.class, Mockito.CALLS_REAL_METHODS); + clientUtils.when(KubernetesClientUtils::kubernetesApiClient).thenReturn(client); + } + + @AfterAll + static void afterAll() { + wireMockServer.stop(); + serviceAMockServer.stop(); + MOCKED_STATIC.close(); + clientUtils.close(); + } + + /** + *
+	 *      - caching is enabled and : 'spring.cloud.loadbalancer.cache.ttl=2s'
+	 *      - we make two calls within those two seconds
+	 *      - as such, first loadBalancer.choose() will execute on the delegate,
+	 *        while the second one will be cached.
+	 *
+	 * 
+ */ + @Test + void test() { + + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance("service-a"); + Response firstResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(firstResponse.hasServer()).isTrue(); + Response secondResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(secondResponse.hasServer()).isTrue(); + + // called two times + wireMockServer.verify(WireMock.exactly(1), WireMock.getRequestedFor( + WireMock.urlEqualTo("/api/v1/namespaces/a/services?fieldSelector=metadata.name%3D" + "service-a"))); + + } + +} diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/AllNamespacesTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/AllNamespacesTest.java index 8ee77ef68..3b5710ac9 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/AllNamespacesTest.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/AllNamespacesTest.java @@ -34,10 +34,11 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.LoadBalancerConfiguration; import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; @@ -49,8 +50,6 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.mockito.Mockito.mockStatic; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -58,7 +57,7 @@ @SpringBootTest( properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=true" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class AllNamespacesTest { private static final String SERVICE_A_URL = "http://service-a"; @@ -77,6 +76,7 @@ class AllNamespacesTest { private static MockedStatic clientUtils; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -84,7 +84,7 @@ class AllNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -158,7 +158,6 @@ void test() { Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("service-a", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()) diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java index 245733aee..f5195faa4 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java @@ -34,10 +34,11 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.LoadBalancerConfiguration; import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; @@ -49,8 +50,6 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.mockito.Mockito.mockStatic; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -58,7 +57,7 @@ @SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.discovery.namespaces.[0]=a", "spring.cloud.kubernetes.discovery.namespaces.[1]=b" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class SelectiveNamespacesTest { private static final String MY_SERVICE_URL = "http://my-service"; @@ -77,6 +76,7 @@ class SelectiveNamespacesTest { private static WireMockServer serviceCMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -86,7 +86,7 @@ class SelectiveNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -182,7 +182,6 @@ void test() { } CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()) diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SpecificNamespaceTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SpecificNamespaceTest.java index a1aa42053..d67d7039d 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SpecificNamespaceTest.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/pod/SpecificNamespaceTest.java @@ -34,10 +34,11 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.LoadBalancerConfiguration; import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; @@ -49,15 +50,13 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.mockito.Mockito.mockStatic; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 */ @SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class SpecificNamespaceTest { private static final String SERVICE_A_URL = "http://my-service"; @@ -72,6 +71,7 @@ class SpecificNamespaceTest { private static WireMockServer serviceBMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -81,7 +81,7 @@ class SpecificNamespaceTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -151,7 +151,6 @@ void test() { Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()) diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/AllNamespacesTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/AllNamespacesTest.java index 6876b71a6..33995f701 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/AllNamespacesTest.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/AllNamespacesTest.java @@ -34,11 +34,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; import org.springframework.cloud.kubernetes.client.loadbalancer.KubernetesClientServicesListSupplier; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.LoadBalancerConfiguration; import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; @@ -49,8 +50,6 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.mockito.Mockito.mockStatic; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -58,7 +57,7 @@ @SpringBootTest( properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=true" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class AllNamespacesTest { private static final String SERVICE_A_URL = "http://service-a"; @@ -77,6 +76,7 @@ class AllNamespacesTest { private static MockedStatic clientUtils; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -84,7 +84,7 @@ class AllNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -158,7 +158,6 @@ void test() { Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("service-a", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(KubernetesClientServicesListSupplier.class); diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SelectiveNamespacesTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SelectiveNamespacesTest.java index 6c30805da..17760b010 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SelectiveNamespacesTest.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SelectiveNamespacesTest.java @@ -34,11 +34,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; import org.springframework.cloud.kubernetes.client.loadbalancer.KubernetesClientServicesListSupplier; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.LoadBalancerConfiguration; import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; @@ -49,8 +50,6 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.mockito.Mockito.mockStatic; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -58,7 +57,7 @@ @SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.discovery.namespaces.[0]=a", "spring.cloud.kubernetes.discovery.namespaces.[1]=b" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class SelectiveNamespacesTest { private static final String MY_SERVICE_URL = "http://my-service"; @@ -77,6 +76,7 @@ class SelectiveNamespacesTest { private static WireMockServer serviceCMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -86,7 +86,7 @@ class SelectiveNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -182,7 +182,6 @@ void test() { } CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(KubernetesClientServicesListSupplier.class); diff --git a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SpecificNamespaceTest.java b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SpecificNamespaceTest.java index 03502d9b8..0530ac29f 100644 --- a/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SpecificNamespaceTest.java +++ b/spring-cloud-kubernetes-client-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/client/loadbalancer/it/mode/service/SpecificNamespaceTest.java @@ -34,11 +34,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.client.KubernetesClientUtils; import org.springframework.cloud.kubernetes.client.loadbalancer.KubernetesClientServicesListSupplier; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.App; +import org.springframework.cloud.kubernetes.client.loadbalancer.it.LoadBalancerConfiguration; import org.springframework.cloud.kubernetes.client.loadbalancer.it.Util; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; @@ -49,8 +50,6 @@ import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; import static org.mockito.Mockito.mockStatic; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.client.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -59,7 +58,7 @@ properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class SpecificNamespaceTest { private static final String SERVICE_A_URL = "http://my-service"; @@ -74,6 +73,7 @@ class SpecificNamespaceTest { private static WireMockServer serviceBMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -83,7 +83,7 @@ class SpecificNamespaceTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -91,6 +91,7 @@ static void beforeAll() { wireMockServer = new WireMockServer(options().dynamicPort()); wireMockServer.start(); WireMock.configureFor("localhost", wireMockServer.port()); + mockWatchers(); serviceAMockServer = new WireMockServer(SERVICE_A_PORT); @@ -153,7 +154,6 @@ void test() { Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(KubernetesClientServicesListSupplier.class); diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/Fabric8ServicesListSupplier.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/Fabric8ServicesListSupplier.java index cfb0b48f2..57462019a 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/Fabric8ServicesListSupplier.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/main/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/Fabric8ServicesListSupplier.java @@ -57,51 +57,53 @@ public class Fabric8ServicesListSupplier extends KubernetesServicesListSupplier< @Override public Flux> get() { - List result = new ArrayList<>(); - String serviceName = getServiceId(); - LOG.debug(() -> "serviceID : " + serviceName); + return Flux.defer(() -> { + List result = new ArrayList<>(); + String serviceName = getServiceId(); + LOG.debug(() -> "serviceID : " + serviceName); - if (discoveryProperties.allNamespaces()) { - LOG.debug(() -> "discovering services in all namespaces"); - List services = kubernetesClient.services() - .inAnyNamespace() - .withField("metadata.name", serviceName) - .list() - .getItems(); - services.forEach(service -> addMappedService(mapper, result, service)); - } - else if (!discoveryProperties.namespaces().isEmpty()) { - List selectiveNamespaces = discoveryProperties.namespaces().stream().sorted().toList(); - LOG.debug(() -> "discovering services in selective namespaces : " + selectiveNamespaces); - selectiveNamespaces.forEach(selectiveNamespace -> { - Service service = kubernetesClient.services() - .inNamespace(selectiveNamespace) - .withName(serviceName) - .get(); + if (discoveryProperties.allNamespaces()) { + LOG.debug(() -> "discovering services in all namespaces"); + List services = kubernetesClient.services() + .inAnyNamespace() + .withField("metadata.name", serviceName) + .list() + .getItems(); + services.forEach(service -> addMappedService(mapper, result, service)); + } + else if (!discoveryProperties.namespaces().isEmpty()) { + List selectiveNamespaces = discoveryProperties.namespaces().stream().sorted().toList(); + LOG.debug(() -> "discovering services in selective namespaces : " + selectiveNamespaces); + selectiveNamespaces.forEach(selectiveNamespace -> { + Service service = kubernetesClient.services() + .inNamespace(selectiveNamespace) + .withName(serviceName) + .get(); + if (service != null) { + addMappedService(mapper, result, service); + } + else { + LOG.debug(() -> "did not find service with name : " + serviceName + " in namespace : " + + selectiveNamespace); + } + }); + } + else { + String namespace = Fabric8Utils.getApplicationNamespace(kubernetesClient, null, "loadbalancer-service", + namespaceProvider); + LOG.debug(() -> "discovering services in namespace : " + namespace); + Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get(); if (service != null) { addMappedService(mapper, result, service); } else { - LOG.debug(() -> "did not find service with name : " + serviceName + " in namespace : " - + selectiveNamespace); + LOG.debug(() -> "did not find service with name : " + serviceName + " in namespace : " + namespace); } - }); - } - else { - String namespace = Fabric8Utils.getApplicationNamespace(kubernetesClient, null, "loadbalancer-service", - namespaceProvider); - LOG.debug(() -> "discovering services in namespace : " + namespace); - Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get(); - if (service != null) { - addMappedService(mapper, result, service); - } - else { - LOG.debug(() -> "did not find service with name : " + serviceName + " in namespace : " + namespace); } - } - LOG.debug(() -> "found services : " + result); - return Flux.just(result); + LOG.debug(() -> "found services : " + result); + return Flux.just(result); + }); } private void addMappedService(KubernetesServiceInstanceMapper mapper, List services, diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/Util.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/Util.java index d99bf24ff..a8b1c340c 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/Util.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/Util.java @@ -27,13 +27,6 @@ import io.fabric8.kubernetes.api.model.ServicePortBuilder; import io.fabric8.kubernetes.api.model.ServiceSpecBuilder; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.web.reactive.function.client.WebClient; - /** * @author wind57 */ @@ -63,24 +56,4 @@ public static Endpoints endpoints(int port, String host, String namespace) { .build(); } - @TestConfiguration - public static class LoadBalancerConfiguration { - - @Bean - @LoadBalanced - WebClient.Builder client() { - return WebClient.builder(); - } - - } - - @SpringBootApplication - public static class Configuration { - - public static void main(String[] args) { - SpringApplication.run(Configuration.class); - } - - } - } diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/App.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/App.java new file mode 100644 index 000000000..f319abc8f --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/App.java @@ -0,0 +1,29 @@ +/* + * 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.loadbalancer.it.mode; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + + public static void main(String[] args) { + SpringApplication.run(App.class); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/LoadBalancerConfiguration.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/LoadBalancerConfiguration.java new file mode 100644 index 000000000..34ec022c3 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/LoadBalancerConfiguration.java @@ -0,0 +1,33 @@ +/* + * 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.loadbalancer.it.mode; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.function.client.WebClient; + +@TestConfiguration +public class LoadBalancerConfiguration { + + @Bean + @LoadBalanced + WebClient.Builder client() { + return WebClient.builder(); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheDisabledTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheDisabledTest.java new file mode 100644 index 000000000..d7470edca --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheDisabledTest.java @@ -0,0 +1,129 @@ +/* + * 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.loadbalancer.it.mode.cache; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.test.annotation.DirtiesContext; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author wind57 + */ +@SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", + "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", + "spring.cloud.kubernetes.client.namespace=a", "spring.cloud.loadbalancer.cache.enabled=false" }, + classes = App.class) +@DirtiesContext +class CacheDisabledTest { + + private static final int SERVICE_PORT = 8888; + + private static WireMockServer wireMockServer; + + private static WireMockServer serviceAMockServer; + + @SuppressWarnings("rawtypes") + private static final MockedStatic MOCKED_STATIC = Mockito + .mockStatic(KubernetesServiceInstanceMapper.class); + + @Autowired + private LoadBalancerClientFactory loadBalancerClientFactory; + + @BeforeAll + static void beforeAll() { + + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + serviceAMockServer = new WireMockServer(SERVICE_PORT); + serviceAMockServer.start(); + WireMock.configureFor("localhost", SERVICE_PORT); + + // we mock host creation so that it becomes something like : localhost:8888 + // then wiremock can catch this request, and we can assert for the result + MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local")) + .thenReturn("localhost"); + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://localhost:" + wireMockServer.port()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test"); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + } + + @AfterAll + static void afterAll() { + wireMockServer.stop(); + serviceAMockServer.stop(); + MOCKED_STATIC.close(); + } + + /** + *
+	 *      - we disable caching via 'spring.cloud.loadbalancer.cache.enabled=false'
+	 *      - as such, two calls to : loadBalancer.choose() will both execute
+	 *        on the delegate itself, which we assert via 'wireMockServer.verify'
+	 * 
+ */ + @Test + void test() { + + Service serviceA = Util.service("a", "my-service", SERVICE_PORT); + String serviceAJson = Serialization.asJson(serviceA); + + wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a")) + .willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200))); + + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance("service-a"); + Response firstResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(firstResponse.hasServer()).isTrue(); + + Response secondResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(secondResponse.hasServer()).isTrue(); + + // called two times, since caching is disabled. + wireMockServer.verify(WireMock.exactly(2), + WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a"))); + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheEnabledOutsideTTLTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheEnabledOutsideTTLTest.java new file mode 100644 index 000000000..fe484bb87 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheEnabledOutsideTTLTest.java @@ -0,0 +1,133 @@ +/* + * 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.loadbalancer.it.mode.cache; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.test.annotation.DirtiesContext; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author wind57 + */ +@SpringBootTest( + properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a", + "spring.cloud.loadbalancer.cache.enabled=true", "spring.cloud.loadbalancer.cache.ttl=2s" }, + classes = App.class) +@DirtiesContext +class CacheEnabledOutsideTTLTest { + + private static final int SERVICE_PORT = 8888; + + private static WireMockServer wireMockServer; + + private static WireMockServer serviceAMockServer; + + @SuppressWarnings("rawtypes") + private static final MockedStatic MOCKED_STATIC = Mockito + .mockStatic(KubernetesServiceInstanceMapper.class); + + @Autowired + private LoadBalancerClientFactory loadBalancerClientFactory; + + @BeforeAll + static void beforeAll() { + + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + serviceAMockServer = new WireMockServer(SERVICE_PORT); + serviceAMockServer.start(); + WireMock.configureFor("localhost", SERVICE_PORT); + + // we mock host creation so that it becomes something like : localhost:8888 + // then wiremock can catch this request, and we can assert for the result + MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local")) + .thenReturn("localhost"); + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://localhost:" + wireMockServer.port()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test"); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + } + + @AfterAll + static void afterAll() { + wireMockServer.stop(); + serviceAMockServer.stop(); + MOCKED_STATIC.close(); + + } + + /** + *
+	 *      - caching is enabled and : 'spring.cloud.loadbalancer.cache.ttl=2s'
+	 *      - we make one call now, and one after 2s.
+	 *      - as such, first loadBalancer.choose() will execute on the delegate,
+	 *        it will be cached. And the second call, since it got TTL-ed, will happen
+	 *        on the delegate again.
+	 *
+	 * 
+ */ + @Test + void testCallsOutsideTTL() throws InterruptedException { + + Service serviceA = Util.service("a", "my-service", SERVICE_PORT); + String serviceAJson = Serialization.asJson(serviceA); + + wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a")) + .willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200))); + + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance("service-a"); + Mono.from(loadBalancer.choose()).block(); + // ttl will expire the first flux + Thread.sleep(2500); + Response response = Mono.from(loadBalancer.choose()).block(); + assertThat(response.hasServer()).as("there should be one server").isTrue(); + + // called two times, since it got expired via the ttl + wireMockServer.verify(WireMock.exactly(2), + WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a"))); + } + +} diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheEnabledWithinTTLTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheEnabledWithinTTLTest.java new file mode 100644 index 000000000..975ced4e6 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/cache/CacheEnabledWithinTTLTest.java @@ -0,0 +1,131 @@ +/* + * 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.loadbalancer.it.mode.cache; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.test.annotation.DirtiesContext; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author wind57 + */ +@SpringBootTest( + properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", + "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a", + "spring.cloud.loadbalancer.cache.enabled=true", "spring.cloud.loadbalancer.cache.ttl=2s" }, + classes = App.class) +@DirtiesContext +class CacheEnabledWithinTTLTest { + + private static final int SERVICE_PORT = 8888; + + private static WireMockServer wireMockServer; + + private static WireMockServer serviceAMockServer; + + @SuppressWarnings("rawtypes") + private static final MockedStatic MOCKED_STATIC = Mockito + .mockStatic(KubernetesServiceInstanceMapper.class); + + @Autowired + private LoadBalancerClientFactory loadBalancerClientFactory; + + @BeforeAll + static void beforeAll() { + + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + WireMock.configureFor("localhost", wireMockServer.port()); + + serviceAMockServer = new WireMockServer(SERVICE_PORT); + serviceAMockServer.start(); + WireMock.configureFor("localhost", SERVICE_PORT); + + // we mock host creation so that it becomes something like : localhost:8888 + // then wiremock can catch this request, and we can assert for the result + MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local")) + .thenReturn("localhost"); + + // Configure the kubernetes master url to point to the mock server + System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, "http://localhost:" + wireMockServer.port()); + System.setProperty(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY, "true"); + System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false"); + System.setProperty(Config.KUBERNETES_NAMESPACE_SYSTEM_PROPERTY, "test"); + System.setProperty(Config.KUBERNETES_HTTP2_DISABLE, "true"); + } + + @AfterAll + static void afterAll() { + wireMockServer.stop(); + serviceAMockServer.stop(); + MOCKED_STATIC.close(); + } + + /** + *
+	 *      - caching is enabled and : 'spring.cloud.loadbalancer.cache.ttl=2s'
+	 *      - we make two calls within those two seconds
+	 *      - as such, first loadBalancer.choose() will execute on the delegate,
+	 *        while the second one will be cached.
+	 *
+	 * 
+ */ + @Test + void testCallsWithinTTL() { + + Service serviceA = Util.service("a", "my-service", SERVICE_PORT); + String serviceAJson = Serialization.asJson(serviceA); + + wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a")) + .willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200))); + + ReactiveLoadBalancer loadBalancer = loadBalancerClientFactory.getInstance("service-a"); + Response firstResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(firstResponse.hasServer()).isTrue(); + Response secondResponse = Mono.from(loadBalancer.choose()).block(); + assertThat(secondResponse.hasServer()).isTrue(); + + // called once only + wireMockServer.verify(WireMock.exactly(1), + WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a"))); + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/AllNamespacesTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/AllNamespacesTest.java index 068be8b5e..0394cb777 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/AllNamespacesTest.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/AllNamespacesTest.java @@ -30,11 +30,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.LoadBalancerConfiguration; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; @@ -43,8 +44,6 @@ import org.springframework.web.reactive.function.client.WebClient; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -52,7 +51,7 @@ @SpringBootTest( properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=true" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class AllNamespacesTest { private static final String SERVICE_A_URL = "http://service-a"; @@ -69,6 +68,7 @@ class AllNamespacesTest { private static WireMockServer serviceBMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -76,7 +76,7 @@ class AllNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -177,7 +177,6 @@ void test() { Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("service-a", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()) diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java index 99dcd1a16..c67d3dfb5 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SelectiveNamespacesTest.java @@ -30,11 +30,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.LoadBalancerConfiguration; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; @@ -43,8 +44,6 @@ import org.springframework.web.reactive.function.client.WebClient; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -52,7 +51,7 @@ @SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.discovery.namespaces.[0]=a", "spring.cloud.kubernetes.discovery.namespaces.[1]=b" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class SelectiveNamespacesTest { private static final String MY_SERVICE_URL = "http://my-service"; @@ -71,6 +70,7 @@ class SelectiveNamespacesTest { private static WireMockServer serviceCMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -78,7 +78,7 @@ class SelectiveNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -213,7 +213,6 @@ void test() { } CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()) diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SpecificNamespaceTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SpecificNamespaceTest.java index 3ce96ffdb..27b8c4263 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SpecificNamespaceTest.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/pod/SpecificNamespaceTest.java @@ -30,11 +30,12 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.LoadBalancerConfiguration; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; @@ -43,15 +44,13 @@ import org.springframework.web.reactive.function.client.WebClient; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 */ @SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) class SpecificNamespaceTest { private static final String SERVICE_A_URL = "http://my-service"; @@ -66,6 +65,7 @@ class SpecificNamespaceTest { private static WireMockServer serviceBMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -73,7 +73,7 @@ class SpecificNamespaceTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -170,7 +170,6 @@ void test() { Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()) diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/AllNamespacesTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/AllNamespacesTest.java index 1fa095542..d5af1a0af 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/AllNamespacesTest.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/AllNamespacesTest.java @@ -30,7 +30,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.CapturedOutput; @@ -38,6 +37,8 @@ import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.Fabric8ServicesListSupplier; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.LoadBalancerConfiguration; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; @@ -45,8 +46,6 @@ import org.springframework.web.reactive.function.client.WebClient; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -54,7 +53,7 @@ @SpringBootTest( properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=true" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) @ExtendWith(OutputCaptureExtension.class) class AllNamespacesTest { @@ -72,6 +71,7 @@ class AllNamespacesTest { private static WireMockServer serviceBMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -79,7 +79,7 @@ class AllNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -168,13 +168,11 @@ void test(CapturedOutput output) { Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached"); CachingServiceInstanceListSupplier supplierA = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("service-a", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplierA.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class); CachingServiceInstanceListSupplier supplierB = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("service-b", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplierB.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class); diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SelectiveNamespacesTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SelectiveNamespacesTest.java index d41be6c40..bdb7408cb 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SelectiveNamespacesTest.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SelectiveNamespacesTest.java @@ -29,7 +29,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.CapturedOutput; @@ -37,6 +36,8 @@ import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.Fabric8ServicesListSupplier; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.LoadBalancerConfiguration; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; @@ -44,8 +45,6 @@ import org.springframework.web.reactive.function.client.WebClient; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -53,7 +52,7 @@ @SpringBootTest(properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.discovery.namespaces.[0]=a", "spring.cloud.kubernetes.discovery.namespaces.[1]=b" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) @ExtendWith(OutputCaptureExtension.class) class SelectiveNamespacesTest { @@ -73,6 +72,7 @@ class SelectiveNamespacesTest { private static WireMockServer serviceCMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -80,7 +80,7 @@ class SelectiveNamespacesTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -195,7 +195,6 @@ void test(CapturedOutput output) { } CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class); diff --git a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SpecificNamespaceTest.java b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SpecificNamespaceTest.java index 1014ef4e2..27bc3798e 100644 --- a/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SpecificNamespaceTest.java +++ b/spring-cloud-kubernetes-fabric8-loadbalancer/src/test/java/org/springframework/cloud/kubernetes/fabric8/loadbalancer/it/mode/service/SpecificNamespaceTest.java @@ -29,7 +29,6 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.CapturedOutput; @@ -37,6 +36,8 @@ import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.Fabric8ServicesListSupplier; import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.App; +import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.LoadBalancerConfiguration; import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; @@ -44,8 +45,6 @@ import org.springframework.web.reactive.function.client.WebClient; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration; -import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration; /** * @author wind57 @@ -54,7 +53,7 @@ properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES", "spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a" }, - classes = { LoadBalancerConfiguration.class, Configuration.class }) + classes = { LoadBalancerConfiguration.class, App.class }) @ExtendWith(OutputCaptureExtension.class) class SpecificNamespaceTest { @@ -70,6 +69,7 @@ class SpecificNamespaceTest { private static WireMockServer serviceBMockServer; + @SuppressWarnings("rawtypes") private static final MockedStatic MOCKED_STATIC = Mockito .mockStatic(KubernetesServiceInstanceMapper.class); @@ -77,7 +77,7 @@ class SpecificNamespaceTest { private WebClient.Builder builder; @Autowired - private ObjectProvider loadBalancerClientFactory; + private LoadBalancerClientFactory loadBalancerClientFactory; @BeforeAll static void beforeAll() { @@ -160,7 +160,6 @@ void test(CapturedOutput output) { Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached"); CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory - .getIfAvailable() .getProvider("my-service", ServiceInstanceListSupplier.class) .getIfAvailable(); Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class); diff --git a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java index 6be976466..7fcb1c33d 100644 --- a/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java +++ b/spring-cloud-kubernetes-test-support/src/main/java/org/springframework/cloud/kubernetes/integration/tests/commons/Commons.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.TimeUnit; import com.github.dockerjava.api.command.ListImagesCmd; import com.github.dockerjava.api.command.PullImageCmd; @@ -36,7 +35,6 @@ import com.github.dockerjava.api.model.Image; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.assertj.core.api.Assertions; import org.testcontainers.containers.Container; import org.testcontainers.k3s.K3sContainer; import reactor.netty.http.client.HttpClient; @@ -79,54 +77,6 @@ public static void loadSpringCloudKubernetesImage(String project, K3sContainer c loadImage("springcloud/" + project, pomVersion(), project, container); } - /** - * assert that "left" is present and if so, "right" is not. - */ - public static void assertReloadLogStatements(String left, String right, String appLabel) { - - try { - String appPodName = CONTAINER - .execInContainer("sh", "-c", - "kubectl get pods -l app=" + appLabel + " -o=name --no-headers | tr -d '\n'") - .getStdout(); - LOG.info("appPodName : ->" + appPodName + "<-"); - // we issue a pollDelay to let the logs sync in, otherwise the results are not - // going to be correctly asserted - await().pollDelay(20, TimeUnit.SECONDS) - .pollInterval(Duration.ofSeconds(5)) - .atMost(Duration.ofSeconds(120)) - .until(() -> { - - Container.ExecResult result = CONTAINER.execInContainer("sh", "-c", - "kubectl logs " + appPodName.trim() + "| grep " + "'" + left + "'"); - String error = result.getStderr(); - String ok = result.getStdout(); - - LOG.info("error is : -->" + error + "<--"); - - if (ok != null && !ok.isBlank()) { - - if (!right.isBlank()) { - String notPresent = CONTAINER - .execInContainer("sh", "-c", - "kubectl logs " + appPodName.trim() + "| grep " + "'" + right + "'") - .getStdout(); - - Assertions.assertThat(notPresent).isNullOrEmpty(); - } - - return true; - } - LOG.info("log statement not yet present"); - return false; - }); - } - catch (Exception e) { - throw new RuntimeException(e); - } - - } - /** * create a tar, copy it in the running k3s and load this tar as an image. */