Add selective namespaces to fabric8 loadbalancer (#1604)
This commit is contained in:
@@ -27,6 +27,7 @@ import reactor.core.publisher.Flux;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
|
||||
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
|
||||
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper;
|
||||
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServicesListSupplier;
|
||||
import org.springframework.cloud.kubernetes.fabric8.Fabric8Utils;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
@@ -64,7 +65,22 @@ public class Fabric8ServicesListSupplier extends KubernetesServicesListSupplier<
|
||||
LOG.debug(() -> "discovering services in all namespaces");
|
||||
List<Service> services = kubernetesClient.services().inAnyNamespace()
|
||||
.withField("metadata.name", serviceName).list().getItems();
|
||||
services.forEach(service -> result.add(mapper.map(service)));
|
||||
services.forEach(service -> addMappedService(mapper, result, service));
|
||||
}
|
||||
else if (!discoveryProperties.namespaces().isEmpty()) {
|
||||
List<String> 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",
|
||||
@@ -72,7 +88,7 @@ public class Fabric8ServicesListSupplier extends KubernetesServicesListSupplier<
|
||||
LOG.debug(() -> "discovering services in namespace : " + namespace);
|
||||
Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get();
|
||||
if (service != null) {
|
||||
result.add(mapper.map(service));
|
||||
addMappedService(mapper, result, service);
|
||||
}
|
||||
else {
|
||||
LOG.debug(() -> "did not find service with name : " + serviceName + " in namespace : " + namespace);
|
||||
@@ -83,4 +99,9 @@ public class Fabric8ServicesListSupplier extends KubernetesServicesListSupplier<
|
||||
return Flux.defer(() -> Flux.just(result));
|
||||
}
|
||||
|
||||
private void addMappedService(KubernetesServiceInstanceMapper<Service> mapper, List<ServiceInstance> services,
|
||||
Service service) {
|
||||
services.add(mapper.map(service));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,18 +88,18 @@ class Fabric8ServicesListSupplierMockClientTests {
|
||||
|
||||
List<List<ServiceInstance>> serviceInstances = supplier.get().collectList().block();
|
||||
Assertions.assertEquals(serviceInstances.size(), 1);
|
||||
List<ServiceInstance> inner = serviceInstances.get(0);
|
||||
|
||||
List<ServiceInstance> serviceInstancesSorted = serviceInstances.get(0).stream()
|
||||
.sorted(Comparator.comparing(ServiceInstance::getServiceId)).toList();
|
||||
Assertions.assertEquals(serviceInstancesSorted.size(), 2);
|
||||
Assertions.assertEquals(inner.get(0).getServiceId(), "service-a");
|
||||
Assertions.assertEquals(inner.get(0).getHost(), "service-a.a.svc.cluster.local");
|
||||
Assertions.assertEquals(inner.get(0).getPort(), 8887);
|
||||
|
||||
Assertions.assertEquals(inner.get(1).getServiceId(), "service-a");
|
||||
Assertions.assertEquals(inner.get(1).getHost(), "service-a.c.svc.cluster.local");
|
||||
Assertions.assertEquals(inner.get(1).getPort(), 8889);
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(0).getServiceId(), "service-a");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(0).getHost(), "service-a.a.svc.cluster.local");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(0).getPort(), 8887);
|
||||
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(1).getServiceId(), "service-a");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(1).getHost(), "service-a.c.svc.cluster.local");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(1).getPort(), 8889);
|
||||
|
||||
Assertions.assertTrue(output.getOut().contains("discovering services in all namespaces"));
|
||||
}
|
||||
@@ -138,6 +138,42 @@ class Fabric8ServicesListSupplierMockClientTests {
|
||||
Assertions.assertTrue(output.getOut().contains("discovering services in namespace : c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSelectiveNamespaces(CapturedOutput output) {
|
||||
|
||||
createService("a", "my-service", 8887);
|
||||
createService("b", "my-service", 8888);
|
||||
createService("c", "my-service", 8889);
|
||||
|
||||
Environment environment = new MockEnvironment().withProperty("loadbalancer.client.name", "my-service");
|
||||
boolean allNamespaces = false;
|
||||
Set<String> selectiveNamespaces = Set.of("a", "b");
|
||||
|
||||
KubernetesLoadBalancerProperties loadBalancerProperties = new KubernetesLoadBalancerProperties();
|
||||
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties(true, allNamespaces,
|
||||
selectiveNamespaces, true, 60, false, null, Set.of(), Map.of(), null,
|
||||
KubernetesDiscoveryProperties.Metadata.DEFAULT, 0, false, false, null);
|
||||
|
||||
Fabric8ServicesListSupplier supplier = new Fabric8ServicesListSupplier(environment, mockClient,
|
||||
new Fabric8ServiceInstanceMapper(loadBalancerProperties, discoveryProperties), discoveryProperties);
|
||||
|
||||
List<List<ServiceInstance>> serviceInstances = supplier.get().collectList().block();
|
||||
Assertions.assertEquals(serviceInstances.size(), 1);
|
||||
|
||||
List<ServiceInstance> serviceInstancesSorted = serviceInstances.get(0).stream()
|
||||
.sorted(Comparator.comparing(ServiceInstance::getPort)).toList();
|
||||
Assertions.assertEquals(serviceInstancesSorted.size(), 2);
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(0).getServiceId(), "my-service");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(0).getHost(), "my-service.a.svc.cluster.local");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(0).getPort(), 8887);
|
||||
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(1).getServiceId(), "my-service");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(1).getHost(), "my-service.b.svc.cluster.local");
|
||||
Assertions.assertEquals(serviceInstancesSorted.get(1).getPort(), 8888);
|
||||
|
||||
Assertions.assertTrue(output.getOut().contains("discovering services in selective namespaces : [a, b]"));
|
||||
}
|
||||
|
||||
private void createService(String namespace, String name, int port) {
|
||||
Service service = new ServiceBuilder().withNewMetadata().withNamespace(namespace).withName(name).endMetadata()
|
||||
.withSpec(new ServiceSpecBuilder()
|
||||
|
||||
@@ -16,25 +16,65 @@
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddressBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointPortBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointSubsetBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.api.model.ServiceBuilder;
|
||||
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
|
||||
*/
|
||||
final class Util {
|
||||
public final class Util {
|
||||
|
||||
private Util() {
|
||||
|
||||
}
|
||||
|
||||
static Service createService(String namespace, String name, int port) {
|
||||
public static Service service(String namespace, String name, int port) {
|
||||
return new ServiceBuilder().withNewMetadata().withNamespace(namespace).withName(name).endMetadata()
|
||||
.withSpec(new ServiceSpecBuilder()
|
||||
.withPorts(new ServicePortBuilder().withName("http").withPort(port).build()).build())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Endpoints endpoints(int port, String host, String namespace) {
|
||||
return new EndpointsBuilder()
|
||||
.withSubsets(new EndpointSubsetBuilder().withPorts(new EndpointPortBuilder().withPort(port).build())
|
||||
.withAddresses(new EndpointAddressBuilder().withIp(host).build()).build())
|
||||
.withMetadata(new ObjectMetaBuilder().withName("random-name").withNamespace(namespace).build()).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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,17 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it;
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.pod;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddressBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointPortBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointSubsetBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsListBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.utils.Serialization;
|
||||
@@ -37,23 +32,19 @@ import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper;
|
||||
import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util;
|
||||
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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.PodModeAllNamespacesTest.Configuration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.PodModeAllNamespacesTest.LoadBalancerConfiguration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
@@ -62,7 +53,7 @@ import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.PodMo
|
||||
properties = { "spring.cloud.kubernetes.loadbalancer.mode=POD", "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.cloud.kubernetes.discovery.all-namespaces=true" },
|
||||
classes = { LoadBalancerConfiguration.class, Configuration.class })
|
||||
class PodModeAllNamespacesTest {
|
||||
class AllNamespacesTest {
|
||||
|
||||
private static final String SERVICE_A_URL = "http://service-a";
|
||||
|
||||
@@ -137,22 +128,11 @@ class PodModeAllNamespacesTest {
|
||||
@Test
|
||||
void test() {
|
||||
|
||||
Service serviceA = Util.createService("a", "service-a", SERVICE_A_PORT);
|
||||
Service serviceB = Util.createService("b", "service-b", SERVICE_B_PORT);
|
||||
Service serviceA = Util.service("a", "service-a", SERVICE_A_PORT);
|
||||
Service serviceB = Util.service("b", "service-b", SERVICE_B_PORT);
|
||||
|
||||
Endpoints endpointsA = new EndpointsBuilder()
|
||||
.withSubsets(new EndpointSubsetBuilder()
|
||||
.withPorts(new EndpointPortBuilder().withPort(SERVICE_A_PORT).build())
|
||||
.withAddresses(new EndpointAddressBuilder().withIp("127.0.0.1").build()).build())
|
||||
.withMetadata(new ObjectMetaBuilder().withName("no-port-name-service").withNamespace("a").build())
|
||||
.build();
|
||||
|
||||
Endpoints endpointsB = new EndpointsBuilder()
|
||||
.withSubsets(new EndpointSubsetBuilder()
|
||||
.withPorts(new EndpointPortBuilder().withPort(SERVICE_B_PORT).build())
|
||||
.withAddresses(new EndpointAddressBuilder().withIp("127.0.0.1").build()).build())
|
||||
.withMetadata(new ObjectMetaBuilder().withName("no-port-name-service").withNamespace("b").build())
|
||||
.build();
|
||||
Endpoints endpointsA = Util.endpoints(SERVICE_A_PORT, "127.0.0.1", "a");
|
||||
Endpoints endpointsB = Util.endpoints(SERVICE_B_PORT, "127.0.0.1", "b");
|
||||
|
||||
String endpointsAListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsA).build());
|
||||
String endpointsBListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsB).build());
|
||||
@@ -192,26 +172,18 @@ class PodModeAllNamespacesTest {
|
||||
.getIfAvailable().getProvider("service-a", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplier.getDelegate().getClass())
|
||||
.isSameAs(DiscoveryClientServiceInstanceListSupplier.class);
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class LoadBalancerConfiguration {
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock
|
||||
.getRequestedFor(WireMock.urlEqualTo("/api/v1/endpoints?fieldSelector=metadata.name%3Dservice-a")));
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
WebClient.Builder client() {
|
||||
return WebClient.builder();
|
||||
}
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock
|
||||
.getRequestedFor(WireMock.urlEqualTo("/api/v1/endpoints?fieldSelector=metadata.name%3Dservice-b")));
|
||||
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
static class Configuration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServiceModeAllNamespacesTest.Configuration.class);
|
||||
}
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/b/services/service-b")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright 2013-2024 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.pod;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsListBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.utils.Serialization;
|
||||
import org.assertj.core.api.Assertions;
|
||||
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 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.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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.discovery.namespaces.[0]=a",
|
||||
"spring.cloud.kubernetes.discovery.namespaces.[1]=b" },
|
||||
classes = { LoadBalancerConfiguration.class, Configuration.class })
|
||||
class SelectiveNamespacesTest {
|
||||
|
||||
private static final String MY_SERVICE_URL = "http://my-service";
|
||||
|
||||
private static final int SERVICE_A_PORT = 8887;
|
||||
|
||||
private static final int SERVICE_B_PORT = 8888;
|
||||
|
||||
private static final int SERVICE_C_PORT = 8889;
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
private static WireMockServer serviceAMockServer;
|
||||
|
||||
private static WireMockServer serviceBMockServer;
|
||||
|
||||
private static WireMockServer serviceCMockServer;
|
||||
|
||||
private static final MockedStatic<KubernetesServiceInstanceMapper> MOCKED_STATIC = Mockito
|
||||
.mockStatic(KubernetesServiceInstanceMapper.class);
|
||||
|
||||
@Autowired
|
||||
private WebClient.Builder builder;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<LoadBalancerClientFactory> loadBalancerClientFactory;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor("localhost", wireMockServer.port());
|
||||
|
||||
serviceAMockServer = new WireMockServer(SERVICE_A_PORT);
|
||||
serviceAMockServer.start();
|
||||
WireMock.configureFor("localhost", SERVICE_A_PORT);
|
||||
|
||||
serviceBMockServer = new WireMockServer(SERVICE_B_PORT);
|
||||
serviceBMockServer.start();
|
||||
WireMock.configureFor("localhost", SERVICE_B_PORT);
|
||||
|
||||
serviceCMockServer = new WireMockServer(SERVICE_C_PORT);
|
||||
serviceCMockServer.start();
|
||||
WireMock.configureFor("localhost", SERVICE_C_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");
|
||||
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "b", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "c", "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();
|
||||
serviceBMockServer.stop();
|
||||
serviceCMockServer.stop();
|
||||
MOCKED_STATIC.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - my-service is present in 'a' namespace
|
||||
* - my-service is present in 'b' namespace
|
||||
* - my-service is present in 'c' namespace
|
||||
* - we enable search in selective namespaces [a, b]
|
||||
* - load balancer mode is 'POD'
|
||||
*
|
||||
* - as such, only service in namespace a and b are load balanced
|
||||
* - we also assert the type of ServiceInstanceListSupplier corresponding to the POD mode.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void test() {
|
||||
|
||||
Service serviceA = Util.service("a", "my-service", SERVICE_A_PORT);
|
||||
Service serviceB = Util.service("b", "my-service", SERVICE_B_PORT);
|
||||
Service serviceC = Util.service("c", "my-service", SERVICE_C_PORT);
|
||||
|
||||
Endpoints endpointsA = Util.endpoints(SERVICE_A_PORT, "127.0.0.1", "a");
|
||||
Endpoints endpointsB = Util.endpoints(SERVICE_B_PORT, "127.0.0.1", "b");
|
||||
Endpoints endpointsC = Util.endpoints(SERVICE_C_PORT, "127.0.0.1", "c");
|
||||
|
||||
String serviceAJson = Serialization.asJson(serviceA);
|
||||
String serviceBJson = Serialization.asJson(serviceB);
|
||||
String serviceCJson = Serialization.asJson(serviceC);
|
||||
|
||||
String endpointsAListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsA).build());
|
||||
String endpointsBListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsB).build());
|
||||
String endpointsCListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsC).build());
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceBJson).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/c/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceCJson).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock
|
||||
.get(WireMock.urlEqualTo("/api/v1/namespaces/a/endpoints?fieldSelector=metadata.name%3Dmy-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(endpointsAListAsString).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock
|
||||
.get(WireMock.urlEqualTo("/api/v1/namespaces/b/endpoints?fieldSelector=metadata.name%3Dmy-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(endpointsBListAsString).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock
|
||||
.get(WireMock.urlEqualTo("/api/v1/namespaces/c/endpoints?fieldSelector=metadata.name%3Dmy-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(endpointsCListAsString).withStatus(200)));
|
||||
|
||||
serviceAMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-a-reached").withStatus(200)));
|
||||
|
||||
serviceBMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-b-reached").withStatus(200)));
|
||||
|
||||
serviceCMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-c-reached").withStatus(200)));
|
||||
|
||||
String firstCallResult = builder.baseUrl(MY_SERVICE_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
|
||||
String secondCallResult = builder.baseUrl(MY_SERVICE_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
|
||||
// since selective namespaces is a Set, we need to be careful with assertion order
|
||||
if (firstCallResult.equals("service-a-reached")) {
|
||||
Assertions.assertThat(secondCallResult).isEqualTo("service-b-reached");
|
||||
}
|
||||
else {
|
||||
Assertions.assertThat(firstCallResult).isEqualTo("service-b-reached");
|
||||
Assertions.assertThat(secondCallResult).isEqualTo("service-a-reached");
|
||||
}
|
||||
|
||||
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
.getIfAvailable().getProvider("my-service", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplier.getDelegate().getClass())
|
||||
.isSameAs(DiscoveryClientServiceInstanceListSupplier.class);
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service")));
|
||||
|
||||
// not triggered in namespace 'c' since that is not a selective namespace
|
||||
wireMockServer.verify(WireMock.exactly(0),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/c/services/my-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock.getRequestedFor(
|
||||
WireMock.urlEqualTo("/api/v1/namespaces/a/endpoints?fieldSelector=metadata.name%3Dmy-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock.getRequestedFor(
|
||||
WireMock.urlEqualTo("/api/v1/namespaces/b/endpoints?fieldSelector=metadata.name%3Dmy-service")));
|
||||
|
||||
// not triggered in namespace 'c' since that is not a selective namespace
|
||||
wireMockServer.verify(WireMock.exactly(0), WireMock.getRequestedFor(
|
||||
WireMock.urlEqualTo("/api/v1/namespaces/c/endpoints?fieldSelector=metadata.name%3Dmy-service")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,17 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it;
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.pod;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import io.fabric8.kubernetes.api.model.EndpointAddressBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointPortBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointSubsetBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Endpoints;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsBuilder;
|
||||
import io.fabric8.kubernetes.api.model.EndpointsListBuilder;
|
||||
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
|
||||
import io.fabric8.kubernetes.api.model.Service;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.utils.Serialization;
|
||||
@@ -37,34 +32,29 @@ import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper;
|
||||
import org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util;
|
||||
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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.PodModeSpecificNamespaceTest.Configuration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.PodModeSpecificNamespaceTest.LoadBalancerConfiguration;
|
||||
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",
|
||||
@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 })
|
||||
class PodModeSpecificNamespaceTest {
|
||||
classes = { LoadBalancerConfiguration.class, Configuration.class })
|
||||
class SpecificNamespaceTest {
|
||||
|
||||
private static final String SERVICE_A_URL = "http://service-a";
|
||||
private static final String SERVICE_A_URL = "http://my-service";
|
||||
|
||||
private static final int SERVICE_A_PORT = 8888;
|
||||
|
||||
@@ -77,7 +67,7 @@ class PodModeSpecificNamespaceTest {
|
||||
private static WireMockServer serviceBMockServer;
|
||||
|
||||
private static final MockedStatic<KubernetesServiceInstanceMapper> MOCKED_STATIC = Mockito
|
||||
.mockStatic(KubernetesServiceInstanceMapper.class);
|
||||
.mockStatic(KubernetesServiceInstanceMapper.class);
|
||||
|
||||
@Autowired
|
||||
private WebClient.Builder builder;
|
||||
@@ -102,11 +92,11 @@ class PodModeSpecificNamespaceTest {
|
||||
|
||||
// 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("service-a", "a", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("service-b", "b", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "b", "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());
|
||||
@@ -127,34 +117,23 @@ class PodModeSpecificNamespaceTest {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - service-a is present in 'a' namespace
|
||||
* - service-b is present in 'b' namespace
|
||||
* - my-service is present in 'a' namespace
|
||||
* - my-service is present in 'b' namespace
|
||||
* - we enable search in namespace 'a'
|
||||
* - load balancer mode is 'POD'
|
||||
*
|
||||
* - as such, only service-a service is load balanced
|
||||
* - as such, only my-service in namespace a is load balanced
|
||||
* - we also assert the type of ServiceInstanceListSupplier corresponding to the POD mode.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void test() {
|
||||
|
||||
Service serviceA = Util.createService("a", "service-a", SERVICE_A_PORT);
|
||||
Service serviceB = Util.createService("b", "service-a", SERVICE_B_PORT);
|
||||
Service serviceA = Util.service("a", "my-service", SERVICE_A_PORT);
|
||||
Service serviceB = Util.service("b", "my-service", SERVICE_B_PORT);
|
||||
|
||||
Endpoints endpointsA = new EndpointsBuilder()
|
||||
.withSubsets(new EndpointSubsetBuilder()
|
||||
.withPorts(new EndpointPortBuilder().withPort(SERVICE_A_PORT).build())
|
||||
.withAddresses(new EndpointAddressBuilder().withIp("127.0.0.1").build()).build())
|
||||
.withMetadata(new ObjectMetaBuilder().withName("no-port-name-service").withNamespace("a").build())
|
||||
.build();
|
||||
|
||||
Endpoints endpointsB = new EndpointsBuilder()
|
||||
.withSubsets(new EndpointSubsetBuilder()
|
||||
.withPorts(new EndpointPortBuilder().withPort(SERVICE_B_PORT).build())
|
||||
.withAddresses(new EndpointAddressBuilder().withIp("127.0.0.1").build()).build())
|
||||
.withMetadata(new ObjectMetaBuilder().withName("no-port-name-service").withNamespace("b").build())
|
||||
.build();
|
||||
Endpoints endpointsA = Util.endpoints(SERVICE_A_PORT, "127.0.0.1", "a");
|
||||
Endpoints endpointsB = Util.endpoints(SERVICE_B_PORT, "127.0.0.1", "b");
|
||||
|
||||
String endpointsAListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsA).build());
|
||||
String endpointsBListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsB).build());
|
||||
@@ -162,55 +141,46 @@ class PodModeSpecificNamespaceTest {
|
||||
String serviceAString = Serialization.asJson(serviceA);
|
||||
String serviceBString = Serialization.asJson(serviceB);
|
||||
|
||||
wireMockServer
|
||||
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/endpoints?fieldSelector=metadata.name%3Dservice-a"))
|
||||
wireMockServer.stubFor(WireMock
|
||||
.get(WireMock.urlEqualTo("/api/v1/namespaces/a/endpoints?fieldSelector=metadata.name%3Dmy-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(endpointsAListAsString).withStatus(200)));
|
||||
|
||||
wireMockServer
|
||||
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/endpoints?fieldSelector=metadata.name%3Dservice-b"))
|
||||
wireMockServer.stubFor(WireMock
|
||||
.get(WireMock.urlEqualTo("/api/v1/namespaces/b/endpoints?fieldSelector=metadata.name%3Dmy-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(endpointsBListAsString).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceAString).withStatus(200)));
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceAString).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/service-a"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceBString).withStatus(200)));
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceBString).withStatus(200)));
|
||||
|
||||
serviceAMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-a-reached").withStatus(200)));
|
||||
.willReturn(WireMock.aResponse().withBody("service-a-reached").withStatus(200)));
|
||||
|
||||
serviceBMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-b-reached").withStatus(200)));
|
||||
.willReturn(WireMock.aResponse().withBody("service-b-reached").withStatus(200)));
|
||||
|
||||
String serviceAResult = builder.baseUrl(SERVICE_A_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
.bodyToMono(String.class).block();
|
||||
Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached");
|
||||
|
||||
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
.getIfAvailable().getProvider("service-a", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
.getIfAvailable().getProvider("my-service", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplier.getDelegate().getClass())
|
||||
.isSameAs(DiscoveryClientServiceInstanceListSupplier.class);
|
||||
.isSameAs(DiscoveryClientServiceInstanceListSupplier.class);
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock.getRequestedFor(
|
||||
WireMock.urlEqualTo("/api/v1/namespaces/a/endpoints?fieldSelector=metadata.name%3Dmy-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(0), WireMock.getRequestedFor(
|
||||
WireMock.urlEqualTo("/api/v1/namespaces/b/endpoints?fieldSelector=metadata.name%3Dmy-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(0),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/b/services/me-service")));
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class LoadBalancerConfiguration {
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
WebClient.Builder client() {
|
||||
return WebClient.builder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
static class Configuration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServiceModeAllNamespacesTest.Configuration.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it;
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.service;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
@@ -26,28 +26,27 @@ import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
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.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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.ServiceModeAllNamespacesTest.Configuration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.ServiceModeAllNamespacesTest.LoadBalancerConfiguration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.Configuration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Util.LoadBalancerConfiguration;
|
||||
|
||||
/**
|
||||
* @author wind57
|
||||
@@ -56,7 +55,8 @@ import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.Servi
|
||||
properties = { "spring.cloud.kubernetes.loadbalancer.mode=SERVICE", "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.cloud.kubernetes.discovery.all-namespaces=true" },
|
||||
classes = { LoadBalancerConfiguration.class, Configuration.class })
|
||||
class ServiceModeAllNamespacesTest {
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class AllNamespacesTest {
|
||||
|
||||
private static final String SERVICE_A_URL = "http://service-a";
|
||||
|
||||
@@ -129,10 +129,10 @@ class ServiceModeAllNamespacesTest {
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void test() {
|
||||
void test(CapturedOutput output) {
|
||||
|
||||
Service serviceA = Util.createService("a", "service-a", SERVICE_A_PORT);
|
||||
Service serviceB = Util.createService("b", "service-b", SERVICE_B_PORT);
|
||||
Service serviceA = Util.service("a", "service-a", SERVICE_A_PORT);
|
||||
Service serviceB = Util.service("b", "service-b", SERVICE_B_PORT);
|
||||
|
||||
String serviceListAJson = Serialization.asJson(new ServiceListBuilder().withItems(serviceA).build());
|
||||
String serviceListBJson = Serialization.asJson(new ServiceListBuilder().withItems(serviceB).build());
|
||||
@@ -159,29 +159,23 @@ class ServiceModeAllNamespacesTest {
|
||||
.bodyToMono(String.class).block();
|
||||
Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached");
|
||||
|
||||
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
CachingServiceInstanceListSupplier supplierA = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
.getIfAvailable().getProvider("service-a", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
|
||||
}
|
||||
Assertions.assertThat(supplierA.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
|
||||
|
||||
@TestConfiguration
|
||||
static class LoadBalancerConfiguration {
|
||||
CachingServiceInstanceListSupplier supplierB = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
.getIfAvailable().getProvider("service-b", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplierB.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
WebClient.Builder client() {
|
||||
return WebClient.builder();
|
||||
}
|
||||
Assertions.assertThat(output.getOut()).contains("serviceID : service-a");
|
||||
Assertions.assertThat(output.getOut()).contains("serviceID : service-b");
|
||||
Assertions.assertThat(output.getOut()).contains("discovering services in all namespaces");
|
||||
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
static class Configuration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Configuration.class);
|
||||
}
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock
|
||||
.getRequestedFor(WireMock.urlEqualTo("/api/v1/services?fieldSelector=metadata.name%3Dservice-a")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1), WireMock
|
||||
.getRequestedFor(WireMock.urlEqualTo("/api/v1/services?fieldSelector=metadata.name%3Dservice-b")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2013-2024 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.service;
|
||||
|
||||
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.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
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.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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=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 })
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SelectiveNamespacesTest {
|
||||
|
||||
private static final String MY_SERVICE_URL = "http://my-service";
|
||||
|
||||
private static final int SERVICE_A_PORT = 8887;
|
||||
|
||||
private static final int SERVICE_B_PORT = 8888;
|
||||
|
||||
private static final int SERVICE_C_PORT = 8889;
|
||||
|
||||
private static WireMockServer wireMockServer;
|
||||
|
||||
private static WireMockServer serviceAMockServer;
|
||||
|
||||
private static WireMockServer serviceBMockServer;
|
||||
|
||||
private static WireMockServer serviceCMockServer;
|
||||
|
||||
private static final MockedStatic<KubernetesServiceInstanceMapper> MOCKED_STATIC = Mockito
|
||||
.mockStatic(KubernetesServiceInstanceMapper.class);
|
||||
|
||||
@Autowired
|
||||
private WebClient.Builder builder;
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<LoadBalancerClientFactory> loadBalancerClientFactory;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
|
||||
wireMockServer = new WireMockServer(options().dynamicPort());
|
||||
wireMockServer.start();
|
||||
WireMock.configureFor("localhost", wireMockServer.port());
|
||||
|
||||
serviceAMockServer = new WireMockServer(SERVICE_A_PORT);
|
||||
serviceAMockServer.start();
|
||||
WireMock.configureFor("localhost", SERVICE_A_PORT);
|
||||
|
||||
serviceBMockServer = new WireMockServer(SERVICE_B_PORT);
|
||||
serviceBMockServer.start();
|
||||
WireMock.configureFor("localhost", SERVICE_B_PORT);
|
||||
|
||||
serviceCMockServer = new WireMockServer(SERVICE_C_PORT);
|
||||
serviceCMockServer.start();
|
||||
WireMock.configureFor("localhost", SERVICE_C_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");
|
||||
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "b", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "c", "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();
|
||||
serviceBMockServer.stop();
|
||||
serviceCMockServer.stop();
|
||||
MOCKED_STATIC.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - my-service is present in 'a' namespace
|
||||
* - my-service is present in 'b' namespace
|
||||
* - my-service is present in 'c' namespace
|
||||
* - we enable search in selective namespaces [a, b]
|
||||
* - load balancer mode is 'SERVICE'
|
||||
*
|
||||
* - as such, only service in namespace a and b are load balanced
|
||||
* - we also assert the type of ServiceInstanceListSupplier corresponding to the SERVICE mode.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void test(CapturedOutput output) {
|
||||
|
||||
Service serviceA = Util.service("a", "my-service", SERVICE_A_PORT);
|
||||
Service serviceB = Util.service("b", "my-service", SERVICE_B_PORT);
|
||||
Service serviceC = Util.service("c", "my-service", SERVICE_C_PORT);
|
||||
|
||||
String serviceAJson = Serialization.asJson(serviceA);
|
||||
String serviceBJson = Serialization.asJson(serviceB);
|
||||
String serviceCJson = Serialization.asJson(serviceC);
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceBJson).withStatus(200)));
|
||||
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/c/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceCJson).withStatus(200)));
|
||||
|
||||
serviceAMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-a-reached").withStatus(200)));
|
||||
|
||||
serviceBMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-b-reached").withStatus(200)));
|
||||
|
||||
serviceCMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-c-reached").withStatus(200)));
|
||||
|
||||
String firstCallResult = builder.baseUrl(MY_SERVICE_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
|
||||
String secondCallResult = builder.baseUrl(MY_SERVICE_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
|
||||
// since selective namespaces is a Set, we need to be careful with assertion order
|
||||
if (firstCallResult.equals("service-a-reached")) {
|
||||
Assertions.assertThat(secondCallResult).isEqualTo("service-b-reached");
|
||||
}
|
||||
else {
|
||||
Assertions.assertThat(firstCallResult).isEqualTo("service-b-reached");
|
||||
Assertions.assertThat(secondCallResult).isEqualTo("service-a-reached");
|
||||
}
|
||||
|
||||
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
.getIfAvailable().getProvider("my-service", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
|
||||
|
||||
Assertions.assertThat(output.getOut()).contains("serviceID : my-service");
|
||||
Assertions.assertThat(output.getOut()).contains("discovering services in selective namespaces : [a, b]");
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service")));
|
||||
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service")));
|
||||
|
||||
// not triggered in namespace 'c' since that is not a selective namespace
|
||||
wireMockServer.verify(WireMock.exactly(0),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/c/services/my-service")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it;
|
||||
package org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.mode.service;
|
||||
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
@@ -25,39 +25,40 @@ import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
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.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.boot.test.system.CapturedOutput;
|
||||
import org.springframework.boot.test.system.OutputCaptureExtension;
|
||||
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.loadbalancer.core.CachingServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
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.ServiceModeSpecificNamespaceTest.Configuration;
|
||||
import static org.springframework.cloud.kubernetes.fabric8.loadbalancer.it.ServiceModeSpecificNamespaceTest.LoadBalancerConfiguration;
|
||||
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=SERVICE", "spring.main.cloud-platform=KUBERNETES",
|
||||
"spring.cloud.kubernetes.discovery.all-namespaces=false", "spring.cloud.kubernetes.client.namespace=a" },
|
||||
classes = { LoadBalancerConfiguration.class, Configuration.class })
|
||||
class ServiceModeSpecificNamespaceTest {
|
||||
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 })
|
||||
@ExtendWith(OutputCaptureExtension.class)
|
||||
class SpecificNamespaceTest {
|
||||
|
||||
private static final String SERVICE_A_URL = "http://service-a";
|
||||
private static final String MY_SERVICE_URL = "http://my-service";
|
||||
|
||||
private static final int SERVICE_A_PORT = 8888;
|
||||
|
||||
@@ -70,7 +71,7 @@ class ServiceModeSpecificNamespaceTest {
|
||||
private static WireMockServer serviceBMockServer;
|
||||
|
||||
private static final MockedStatic<KubernetesServiceInstanceMapper> MOCKED_STATIC = Mockito
|
||||
.mockStatic(KubernetesServiceInstanceMapper.class);
|
||||
.mockStatic(KubernetesServiceInstanceMapper.class);
|
||||
|
||||
@Autowired
|
||||
private WebClient.Builder builder;
|
||||
@@ -95,11 +96,11 @@ class ServiceModeSpecificNamespaceTest {
|
||||
|
||||
// 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("service-a", "a", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "a", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("service-b", "b", "cluster.local"))
|
||||
.thenReturn("localhost");
|
||||
MOCKED_STATIC.when(() -> KubernetesServiceInstanceMapper.createHost("my-service", "b", "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());
|
||||
@@ -120,64 +121,54 @@ class ServiceModeSpecificNamespaceTest {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* - service-a is present in 'a' namespace
|
||||
* - service-a is present in 'b' namespace
|
||||
* - my-service is present in 'a' namespace
|
||||
* - my-service is present in 'b' namespace
|
||||
* - we enable search in namespace 'a'
|
||||
* - load balancer mode is 'SERVICE'
|
||||
*
|
||||
* - as such, only service-a service is load balanced
|
||||
* - as such, only my-service in namespace a is load balanced
|
||||
* - we also assert the type of ServiceInstanceListSupplier corresponding to the SERVICE mode.
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
void test() {
|
||||
void test(CapturedOutput output) {
|
||||
|
||||
Service serviceA = Util.createService("a", "service-a", SERVICE_A_PORT);
|
||||
Service serviceB = Util.createService("b", "service-a", SERVICE_B_PORT);
|
||||
Service serviceA = Util.service("a", "my-service", SERVICE_A_PORT);
|
||||
Service serviceB = Util.service("b", "my-service", SERVICE_B_PORT);
|
||||
|
||||
String serviceAJson = Serialization.asJson(serviceA);
|
||||
String serviceBJson = Serialization.asJson(serviceB);
|
||||
|
||||
wireMockServer
|
||||
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a"))
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200)));
|
||||
|
||||
wireMockServer
|
||||
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/service-a"))
|
||||
wireMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service"))
|
||||
.willReturn(WireMock.aResponse().withBody(serviceBJson).withStatus(200)));
|
||||
|
||||
serviceAMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-a-reached").withStatus(200)));
|
||||
.willReturn(WireMock.aResponse().withBody("service-a-reached").withStatus(200)));
|
||||
|
||||
serviceBMockServer.stubFor(WireMock.get(WireMock.urlEqualTo("/"))
|
||||
.willReturn(WireMock.aResponse().withBody("service-b-reached").withStatus(200)));
|
||||
.willReturn(WireMock.aResponse().withBody("service-b-reached").withStatus(200)));
|
||||
|
||||
String serviceAResult = builder.baseUrl(SERVICE_A_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
String serviceAResult = builder.baseUrl(MY_SERVICE_URL).build().method(HttpMethod.GET).retrieve()
|
||||
.bodyToMono(String.class).block();
|
||||
Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached");
|
||||
|
||||
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
|
||||
.getIfAvailable().getProvider("service-a", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
.getIfAvailable().getProvider("my-service", ServiceInstanceListSupplier.class).getIfAvailable();
|
||||
Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
|
||||
}
|
||||
|
||||
@TestConfiguration
|
||||
static class LoadBalancerConfiguration {
|
||||
Assertions.assertThat(output.getOut()).contains("serviceID : my-service");
|
||||
Assertions.assertThat(output.getOut()).contains("discovering services in namespace : a");
|
||||
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
WebClient.Builder client() {
|
||||
return WebClient.builder();
|
||||
}
|
||||
// was called in namespace 'a'
|
||||
wireMockServer.verify(WireMock.exactly(1),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/a/services/my-service")));
|
||||
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
static class Configuration {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ServiceModeAllNamespacesTest.Configuration.class);
|
||||
}
|
||||
// was not called in namespace 'b'
|
||||
wireMockServer.verify(WireMock.exactly(0),
|
||||
WireMock.getRequestedFor(WireMock.urlEqualTo("/api/v1/namespaces/b/services/my-service")));
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user