#562 Adding all namespaces handling + tests

This commit is contained in:
piomin
2020-07-03 15:23:05 +02:00
parent 7f31a7739c
commit 694f89add9
6 changed files with 171 additions and 53 deletions

View File

@@ -41,5 +41,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-server-mock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -19,6 +19,7 @@ package org.springframework.cloud.kubernetes.loadbalancer;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
@@ -33,8 +34,10 @@ public class KubernetesClientConfiguration {
@ConditionalOnProperty(name = "spring.cloud.kubernetes.loadbalancer.mode",
havingValue = "SERVICE")
KubernetesServicesListSupplier kubernetesServicesListSupplier(Environment environment,
KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper) {
return new KubernetesServicesListSupplier(environment, kubernetesClient, mapper);
KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper,
KubernetesDiscoveryProperties discoveryProperties) {
return new KubernetesServicesListSupplier(environment, kubernetesClient, mapper,
discoveryProperties);
}
}

View File

@@ -40,6 +40,9 @@ public class KubernetesServiceInstance implements ServiceInstance {
private Map<String, String> metadata;
KubernetesServiceInstance() {
}
KubernetesServiceInstance(String serviceId, String instanceId, int port,
boolean secure, String host, URI uri, Map<String, String> metadata) {
this.serviceId = serviceId;

View File

@@ -25,6 +25,7 @@ import org.apache.commons.lang.StringUtils;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.core.env.Environment;
@@ -38,12 +39,16 @@ public class KubernetesServicesListSupplier implements ServiceInstanceListSuppli
private KubernetesClient kubernetesClient;
private KubernetesDiscoveryProperties discoveryProperties;
private KubernetesServiceInstanceMapper mapper;
KubernetesServicesListSupplier(Environment environment,
KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper) {
KubernetesClient kubernetesClient, KubernetesServiceInstanceMapper mapper,
KubernetesDiscoveryProperties discoveryProperties) {
this.environment = environment;
this.kubernetesClient = kubernetesClient;
this.discoveryProperties = discoveryProperties;
this.mapper = mapper;
}
@@ -55,13 +60,21 @@ public class KubernetesServicesListSupplier implements ServiceInstanceListSuppli
@Override
public Flux<List<ServiceInstance>> get() {
List<ServiceInstance> result = new ArrayList<>();
Service service = StringUtils.isNotBlank(this.kubernetesClient.getNamespace())
? this.kubernetesClient
.services().inNamespace(this.kubernetesClient.getNamespace())
.withName(this.getServiceId()).get()
: this.kubernetesClient.services().withName(this.getServiceId()).get();
if (service != null) {
result.add(mapper.map(service));
if (discoveryProperties.isAllNamespaces()) {
List<Service> services = this.kubernetesClient.services().inAnyNamespace()
.withField("metadata.name", this.getServiceId()).list().getItems();
services.forEach(service -> result.add(mapper.map(service)));
}
else {
Service service = StringUtils.isNotBlank(this.kubernetesClient.getNamespace())
? this.kubernetesClient.services()
.inNamespace(this.kubernetesClient.getNamespace())
.withName(this.getServiceId()).get()
: this.kubernetesClient.services().withName(this.getServiceId())
.get();
if (service != null) {
result.add(mapper.map(service));
}
}
return Flux.just(result);
}

View File

@@ -34,13 +34,12 @@ import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryPropert
public class KubernetesServiceInstanceMapperTest {
@Test
void testMapperSimple() {
public void testMapperSimple() {
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
KubernetesDiscoveryProperties discoveryProperties =
new KubernetesDiscoveryProperties();
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
Service service = buildService("test", "abc", 8080, null, new HashMap<>());
KubernetesServiceInstance instance =
new KubernetesServiceInstanceMapper(properties, discoveryProperties).map(service);
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
properties, discoveryProperties).map(service);
Assertions.assertNotNull(instance);
Assertions.assertEquals("test", instance.getServiceId());
Assertions.assertEquals("abc", instance.getInstanceId());
@@ -50,20 +49,13 @@ public class KubernetesServiceInstanceMapperTest {
void testMapperMultiplePorts() {
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
properties.setPortName("http");
KubernetesDiscoveryProperties discoveryProperties =
new KubernetesDiscoveryProperties();
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
List<ServicePort> ports = new ArrayList<>();
ports.add(new ServicePortBuilder()
.withPort(8080)
.withName("web")
.build());
ports.add(new ServicePortBuilder()
.withPort(9000)
.withName("http")
.build());
ports.add(new ServicePortBuilder().withPort(8080).withName("web").build());
ports.add(new ServicePortBuilder().withPort(9000).withName("http").build());
Service service = buildService("test", "abc", ports, new HashMap<>());
KubernetesServiceInstance instance =
new KubernetesServiceInstanceMapper(properties, discoveryProperties).map(service);
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
properties, discoveryProperties).map(service);
Assertions.assertNotNull(instance);
Assertions.assertEquals("test", instance.getServiceId());
Assertions.assertEquals("abc", instance.getInstanceId());
@@ -73,11 +65,10 @@ public class KubernetesServiceInstanceMapperTest {
@Test
void testMapperSecure() {
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
KubernetesDiscoveryProperties discoveryProperties =
new KubernetesDiscoveryProperties();
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
Service service = buildService("test", "abc", 443, null, new HashMap<>());
KubernetesServiceInstance instance =
new KubernetesServiceInstanceMapper(properties, discoveryProperties).map(service);
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
properties, discoveryProperties).map(service);
Assertions.assertNotNull(instance);
Assertions.assertEquals("test", instance.getServiceId());
Assertions.assertEquals("abc", instance.getInstanceId());
@@ -87,14 +78,13 @@ public class KubernetesServiceInstanceMapperTest {
@Test
void testMapperSecureWithLabels() {
KubernetesLoadBalancerProperties properties = new KubernetesLoadBalancerProperties();
KubernetesDiscoveryProperties discoveryProperties =
new KubernetesDiscoveryProperties();
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
HashMap<String, String> labels = new HashMap<>();
labels.put("secured", "true");
labels.put("label1", "123");
Service service = buildService("test", "abc", 8080, null, labels);
KubernetesServiceInstance instance =
new KubernetesServiceInstanceMapper(properties, discoveryProperties).map(service);
KubernetesServiceInstance instance = new KubernetesServiceInstanceMapper(
properties, discoveryProperties).map(service);
Assertions.assertNotNull(instance);
Assertions.assertEquals("test", instance.getServiceId());
Assertions.assertEquals("abc", instance.getInstanceId());
@@ -102,26 +92,17 @@ public class KubernetesServiceInstanceMapperTest {
Assertions.assertEquals(2, instance.getMetadata().keySet().size());
}
private Service buildService(String name, String uid, List<ServicePort> ports, Map<String, String> labels) {
return new ServiceBuilder()
.withNewMetadata()
.withName(name)
.withNewUid(uid)
.addToLabels(labels)
.addToAnnotations(new HashMap<>(0))
.endMetadata()
.withNewSpec()
.addAllToPorts(ports)
.endSpec()
.build();
private Service buildService(String name, String uid, List<ServicePort> ports,
Map<String, String> labels) {
return new ServiceBuilder().withNewMetadata().withName(name).withNewUid(uid)
.addToLabels(labels).addToAnnotations(new HashMap<>(0)).endMetadata()
.withNewSpec().addAllToPorts(ports).endSpec().build();
}
private Service buildService(String name, String uid, int port, String portName, Map<String, String> labels) {
ServicePort servicePort = new ServicePortBuilder()
.withPort(port)
.withName(portName)
.build();
private Service buildService(String name, String uid, int port, String portName,
Map<String, String> labels) {
ServicePort servicePort = new ServicePortBuilder().withPort(port)
.withName(portName).build();
return buildService(name, uid, Collections.singletonList(servicePort), labels);
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2013-2020 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.loadbalancer;
import java.util.List;
import io.fabric8.kubernetes.api.model.DoneableService;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.ServiceList;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.Watch;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.dsl.FilterWatchListMultiDeletable;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.ServiceResource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.kubernetes.discovery.KubernetesDiscoveryProperties;
import org.springframework.core.env.Environment;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class KubernetesServiceListSupplierTest {
@Mock
Environment environment;
@Mock
KubernetesServiceInstanceMapper mapper;
@Mock
KubernetesClient client;
@Mock
MixedOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> serviceOperation;
@Mock
NonNamespaceOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> namespaceOperation;
@Mock
ServiceResource<Service, DoneableService> serviceResource;
@Mock
FilterWatchListMultiDeletable<Service, ServiceList, Boolean, Watch, Watcher<Service>> multiDeletable;
@Test
void testPositiveMatch() {
when(environment.getProperty("loadbalancer.client.name"))
.thenReturn("test-service");
when(mapper.map(any(Service.class))).thenReturn(new KubernetesServiceInstance());
when(this.client.getNamespace()).thenReturn("test");
when(this.client.services()).thenReturn(this.serviceOperation);
when(this.serviceOperation.inNamespace("test")).thenReturn(namespaceOperation);
when(this.namespaceOperation.withName("test-service"))
.thenReturn(this.serviceResource);
when(this.serviceResource.get()).thenReturn(buildService("test-service", 8080));
KubernetesServicesListSupplier supplier = new KubernetesServicesListSupplier(
environment, client, mapper, new KubernetesDiscoveryProperties());
List<ServiceInstance> instances = supplier.get().blockFirst();
assert instances != null;
Assertions.assertEquals(1, instances.size());
}
@Test
void testPositiveMatchAllNamespaces() {
when(environment.getProperty("loadbalancer.client.name"))
.thenReturn("test-service");
when(mapper.map(any(Service.class))).thenReturn(new KubernetesServiceInstance());
when(this.client.services()).thenReturn(this.serviceOperation);
when(this.serviceOperation.inAnyNamespace()).thenReturn(this.multiDeletable);
when(this.multiDeletable.withField("metadata.name", "test-service"))
.thenReturn(this.multiDeletable);
ServiceList serviceList = new ServiceList();
serviceList.getItems().add(buildService("test-service", 8080));
when(this.multiDeletable.list()).thenReturn(serviceList);
KubernetesDiscoveryProperties discoveryProperties = new KubernetesDiscoveryProperties();
discoveryProperties.setAllNamespaces(true);
KubernetesServicesListSupplier supplier = new KubernetesServicesListSupplier(
environment, client, mapper, discoveryProperties);
List<ServiceInstance> instances = supplier.get().blockFirst();
assert instances != null;
Assertions.assertEquals(1, instances.size());
}
private Service buildService(String name, int port) {
return new ServiceBuilder().withNewMetadata().withName(name).endMetadata()
.withNewSpec().addNewPort().withPort(8080).endPort().endSpec().build();
}
}