Add namespace provider to fabric8 loadbalancer (#1597)

This commit is contained in:
erabii
2024-03-18 15:27:14 +02:00
committed by GitHub
parent 19fa6ab6d6
commit 0bd7bc5580
12 changed files with 1052 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* 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.
@@ -46,6 +46,7 @@ public final class KubernetesClientServicesFunctionProvider {
}
@Deprecated(forRemoval = true)
public static KubernetesClientServicesFunction servicesFunction(KubernetesDiscoveryProperties properties,
Binder binder, BindHandler bindHandler) {
return servicesFunction(properties, new KubernetesNamespaceProvider(binder, bindHandler));

View File

@@ -40,12 +40,35 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.mokito</groupId>
<artifactId>mockito-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-server-mock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-kubernetes-test-support</artifactId>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2020 the original author or authors.
* 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.
@@ -21,14 +21,17 @@ import java.util.List;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.logging.LogFactory;
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.KubernetesServicesListSupplier;
import org.springframework.cloud.kubernetes.fabric8.Fabric8Utils;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.core.log.LogAccessor;
/**
* Implementation of {@link ServiceInstanceListSupplier} for load balancer in SERVICE
@@ -38,30 +41,45 @@ import org.springframework.util.StringUtils;
*/
public class Fabric8ServicesListSupplier extends KubernetesServicesListSupplier<Service> {
private static final LogAccessor LOG = new LogAccessor(LogFactory.getLog(Fabric8ServicesListSupplier.class));
private final KubernetesClient kubernetesClient;
private final KubernetesNamespaceProvider namespaceProvider;
Fabric8ServicesListSupplier(Environment environment, KubernetesClient kubernetesClient,
Fabric8ServiceInstanceMapper mapper, KubernetesDiscoveryProperties discoveryProperties) {
super(environment, mapper, discoveryProperties);
this.kubernetesClient = kubernetesClient;
namespaceProvider = new KubernetesNamespaceProvider(environment);
}
@Override
public Flux<List<ServiceInstance>> get() {
List<ServiceInstance> result = new ArrayList<>();
String serviceName = getServiceId();
LOG.debug(() -> "serviceID : " + serviceName);
if (discoveryProperties.allNamespaces()) {
LOG.debug(() -> "discovering services in all namespaces");
List<Service> services = kubernetesClient.services().inAnyNamespace()
.withField("metadata.name", getServiceId()).list().getItems();
.withField("metadata.name", serviceName).list().getItems();
services.forEach(service -> result.add(mapper.map(service)));
}
else {
Service service = StringUtils.hasText(kubernetesClient.getNamespace()) ? kubernetesClient.services()
.inNamespace(kubernetesClient.getNamespace()).withName(getServiceId()).get()
: kubernetesClient.services().withName(getServiceId()).get();
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) {
result.add(mapper.map(service));
}
else {
LOG.debug(() -> "did not find service with name : " + serviceName + " in namespace : " + namespace);
}
}
LOG.debug(() -> "found services : " + result);
return Flux.defer(() -> Flux.just(result));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* 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.
@@ -78,7 +78,6 @@ class Fabric8ServiceListSupplierTests {
KubernetesServicesListSupplier<Service> supplier = new Fabric8ServicesListSupplier(environment, client, mapper,
KubernetesDiscoveryProperties.DEFAULT);
List<ServiceInstance> instances = supplier.get().blockFirst();
assert instances != null;
Assertions.assertEquals(1, instances.size());
}
@@ -98,7 +97,6 @@ class Fabric8ServiceListSupplierTests {
KubernetesServicesListSupplier<Service> supplier = new Fabric8ServicesListSupplier(environment, client, mapper,
discoveryProperties);
List<ServiceInstance> instances = supplier.get().blockFirst();
assert instances != null;
Assertions.assertEquals(1, instances.size());
}

View File

@@ -0,0 +1,149 @@
/*
* 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;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.commons.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.kubernetes.commons.loadbalancer.KubernetesLoadBalancerProperties;
import org.springframework.core.env.Environment;
import org.springframework.mock.env.MockEnvironment;
/**
* @author wind57
*/
@EnableKubernetesMockClient(crud = true, https = false)
@ExtendWith(OutputCaptureExtension.class)
class Fabric8ServicesListSupplierMockClientTests {
private static KubernetesClient mockClient;
@BeforeAll
static void setUpBeforeClass() {
// Configure the kubernetes master url to point to the mock server
System.setProperty(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY, mockClient.getConfiguration().getMasterUrl());
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");
}
@AfterEach
void afterEach() {
mockClient.services().inAnyNamespace().delete();
}
@Test
void testAllNamespaces(CapturedOutput output) {
createService("a", "service-a", 8887);
createService("b", "service-b", 8888);
createService("c", "service-a", 8889);
Environment environment = new MockEnvironment().withProperty("loadbalancer.client.name", "service-a");
boolean allNamespaces = true;
Set<String> selectiveNamespaces = Set.of();
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> 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.assertTrue(output.getOut().contains("discovering services in all namespaces"));
}
@Test
void testOneNamespace(CapturedOutput output) {
createService("a", "service-c", 8887);
createService("b", "service-b", 8888);
createService("c", "service-c", 8889);
Environment environment = new MockEnvironment().withProperty("spring.cloud.kubernetes.client.namespace", "c")
.withProperty("loadbalancer.client.name", "service-c");
boolean allNamespaces = false;
Set<String> selectiveNamespaces = Set.of();
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> inner = serviceInstances.get(0);
List<ServiceInstance> serviceInstancesSorted = serviceInstances.get(0).stream()
.sorted(Comparator.comparing(ServiceInstance::getServiceId)).toList();
Assertions.assertEquals(serviceInstancesSorted.size(), 1);
Assertions.assertEquals(inner.get(0).getServiceId(), "service-c");
Assertions.assertEquals(inner.get(0).getHost(), "service-c.c.svc.cluster.local");
Assertions.assertEquals(inner.get(0).getPort(), 8889);
Assertions.assertTrue(output.getOut().contains("discovering services in namespace : c"));
}
private void createService(String namespace, String name, int port) {
Service service = new ServiceBuilder().withNewMetadata().withNamespace(namespace).withName(name).endMetadata()
.withSpec(new ServiceSpecBuilder()
.withPorts(new ServicePortBuilder().withName("http").withPort(port).build()).build())
.build();
mockClient.services().resource(service).create();
}
}

View File

@@ -0,0 +1,217 @@
/*
* 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;
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;
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.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.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;
/**
* @author wind57
*/
@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 })
class PodModeAllNamespacesTest {
private static final String SERVICE_A_URL = "http://service-a";
private static final String SERVICE_B_URL = "http://service-b";
private static final int SERVICE_A_PORT = 8888;
private static final int SERVICE_B_PORT = 8889;
private static WireMockServer wireMockServer;
private static WireMockServer serviceAMockServer;
private static WireMockServer serviceBMockServer;
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);
// 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("service-b", "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());
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();
MOCKED_STATIC.close();
}
/**
* <pre>
* - service-a is present in namespace a with exposed port 8888
* - service-b is present in namespace b with exposed port 8889
* - we make two calls to them via the load balancer
* </pre>
*/
@Test
void test() {
Service serviceA = Util.createService("a", "service-a", SERVICE_A_PORT);
Service serviceB = Util.createService("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();
String endpointsAListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsA).build());
String endpointsBListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsB).build());
String serviceAString = Serialization.asJson(serviceA);
String serviceBString = Serialization.asJson(serviceB);
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/endpoints?fieldSelector=metadata.name%3Dservice-a"))
.willReturn(WireMock.aResponse().withBody(endpointsAListAsString).withStatus(200)));
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/endpoints?fieldSelector=metadata.name%3Dservice-b"))
.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/b/services/service-b"))
.willReturn(WireMock.aResponse().withBody(serviceBString).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)));
String serviceAResult = builder.baseUrl(SERVICE_A_URL).build().method(HttpMethod.GET).retrieve()
.bodyToMono(String.class).block();
Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached");
String serviceBResult = builder.baseUrl(SERVICE_B_URL).build().method(HttpMethod.GET).retrieve()
.bodyToMono(String.class).block();
Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached");
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
.getIfAvailable().getProvider("service-a", ServiceInstanceListSupplier.class).getIfAvailable();
Assertions.assertThat(supplier.getDelegate().getClass())
.isSameAs(DiscoveryClientServiceInstanceListSupplier.class);
}
@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);
}
}
}

View File

@@ -0,0 +1,216 @@
/*
* 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;
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;
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.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.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;
/**
* @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 })
class PodModeSpecificNamespaceTest {
private static final String SERVICE_A_URL = "http://service-a";
private static final int SERVICE_A_PORT = 8888;
private static final int SERVICE_B_PORT = 8889;
private static WireMockServer wireMockServer;
private static WireMockServer serviceAMockServer;
private static WireMockServer serviceBMockServer;
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);
// 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("service-b", "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());
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();
MOCKED_STATIC.close();
}
/**
* <pre>
* - service-a is present in 'a' namespace
* - service-b 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
* - 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);
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();
String endpointsAListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsA).build());
String endpointsBListAsString = Serialization.asJson(new EndpointsListBuilder().withItems(endpointsB).build());
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"))
.willReturn(WireMock.aResponse().withBody(endpointsAListAsString).withStatus(200)));
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/endpoints?fieldSelector=metadata.name%3Dservice-b"))
.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/b/services/service-a"))
.willReturn(WireMock.aResponse().withBody(serviceBString).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)));
String serviceAResult = builder.baseUrl(SERVICE_A_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();
Assertions.assertThat(supplier.getDelegate().getClass())
.isSameAs(DiscoveryClientServiceInstanceListSupplier.class);
}
@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);
}
}
}

View File

@@ -0,0 +1,187 @@
/*
* 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;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceListBuilder;
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.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.Fabric8ServicesListSupplier;
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;
/**
* @author wind57
*/
@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 })
class ServiceModeAllNamespacesTest {
private static final String SERVICE_A_URL = "http://service-a";
private static final String SERVICE_B_URL = "http://service-b";
private static final int SERVICE_A_PORT = 8888;
private static final int SERVICE_B_PORT = 8889;
private static WireMockServer wireMockServer;
private static WireMockServer serviceAMockServer;
private static WireMockServer serviceBMockServer;
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);
// 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("service-b", "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());
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();
MOCKED_STATIC.close();
}
/**
* <pre>
* - service-a is present in namespace a with exposed port 8888
* - service-b is present in namespace b with exposed port 8889
* - we make two calls to them via the load balancer
* </pre>
*/
@Test
void test() {
Service serviceA = Util.createService("a", "service-a", SERVICE_A_PORT);
Service serviceB = Util.createService("b", "service-b", SERVICE_B_PORT);
String serviceListAJson = Serialization.asJson(new ServiceListBuilder().withItems(serviceA).build());
String serviceListBJson = Serialization.asJson(new ServiceListBuilder().withItems(serviceB).build());
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/services?fieldSelector=metadata.name%3Dservice-a"))
.willReturn(WireMock.aResponse().withBody(serviceListAJson).withStatus(200)));
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/services?fieldSelector=metadata.name%3Dservice-b"))
.willReturn(WireMock.aResponse().withBody(serviceListBJson).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)));
String serviceAResult = builder.baseUrl(SERVICE_A_URL).build().method(HttpMethod.GET).retrieve()
.bodyToMono(String.class).block();
Assertions.assertThat(serviceAResult).isEqualTo("service-a-reached");
String serviceBResult = builder.baseUrl(SERVICE_B_URL).build().method(HttpMethod.GET).retrieve()
.bodyToMono(String.class).block();
Assertions.assertThat(serviceBResult).isEqualTo("service-b-reached");
CachingServiceInstanceListSupplier supplier = (CachingServiceInstanceListSupplier) loadBalancerClientFactory
.getIfAvailable().getProvider("service-a", ServiceInstanceListSupplier.class).getIfAvailable();
Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
}
@TestConfiguration
static class LoadBalancerConfiguration {
@Bean
@LoadBalanced
WebClient.Builder client() {
return WebClient.builder();
}
}
@SpringBootApplication
static class Configuration {
public static void main(String[] args) {
SpringApplication.run(Configuration.class);
}
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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;
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.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.cloud.kubernetes.commons.loadbalancer.KubernetesServiceInstanceMapper;
import org.springframework.cloud.kubernetes.fabric8.loadbalancer.Fabric8ServicesListSupplier;
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;
/**
* @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 {
private static final String SERVICE_A_URL = "http://service-a";
private static final int SERVICE_A_PORT = 8888;
private static final int SERVICE_B_PORT = 8889;
private static WireMockServer wireMockServer;
private static WireMockServer serviceAMockServer;
private static WireMockServer serviceBMockServer;
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);
// 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("service-b", "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());
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();
MOCKED_STATIC.close();
}
/**
* <pre>
* - service-a is present in 'a' namespace
* - service-a 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
* - we also assert the type of ServiceInstanceListSupplier corresponding to the SERVICE 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);
String serviceAJson = Serialization.asJson(serviceA);
String serviceBJson = Serialization.asJson(serviceB);
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/a/services/service-a"))
.willReturn(WireMock.aResponse().withBody(serviceAJson).withStatus(200)));
wireMockServer
.stubFor(WireMock.get(WireMock.urlEqualTo("/api/v1/namespaces/b/services/service-a"))
.willReturn(WireMock.aResponse().withBody(serviceBJson).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)));
String serviceAResult = builder.baseUrl(SERVICE_A_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();
Assertions.assertThat(supplier.getDelegate().getClass()).isSameAs(Fabric8ServicesListSupplier.class);
}
@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);
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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;
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;
/**
* @author wind57
*/
final class Util {
private Util() {
}
static Service createService(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();
}
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- needed for CapturedOutput -->
<logger name="org.springframework.cloud.kubernetes.fabric8.loadbalancer" level="DEBUG"/>
<logger name="io.fabric8.kubernetes.client.http" level="TRACE" />
</configuration>

View File

@@ -128,7 +128,7 @@
<!-- configuration watcher is based on k8s-client, there's no fabric8 counterpart -->
<module>spring-cloud-kubernetes-k8s-client-configuration-watcher</module>
<!-- at the moment we have no integration test for the fabric8 client, only k8s native client -->
<!-- loadbalancer -->
<module>spring-cloud-kubernetes-k8s-client-loadbalancer</module>
<!-- config watcher reload, using kafka and configmap -->
@@ -137,5 +137,5 @@
<!-- config watcher reload, using rabbitmq and secret -->
<module>spring-cloud-kubernetes-k8s-client-rabbitmq-secret-reload-multiple-apps</module>
</modules>
</modules>
</project>