#54: Fix handling of endpoints with multiple addresses.

This commit is contained in:
Ioannis Canellos
2016-09-09 12:52:44 +03:00
parent fbc1f27115
commit 064af9822b
6 changed files with 142 additions and 26 deletions

11
pom.xml
View File

@@ -89,6 +89,7 @@
<!-- Maven Plugin Versions -->
<maven-compiler-plugin.version>3.5</maven-compiler-plugin.version>
<maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>
<gmavenplus-plugin.version>1.2</gmavenplus-plugin.version>
<sundrio-plugin.vesion>0.3.4</sundrio-plugin.vesion>
</properties>
@@ -262,6 +263,16 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<inherited>true</inherited>
<configuration>
<forkCount>1</forkCount>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
<plugin>
<groupId>io.sundr</groupId>
<artifactId>sundr-maven-plugin</artifactId>

View File

@@ -51,6 +51,38 @@
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -67,7 +67,10 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
return endpoints.getSubsets()
.stream()
.filter(s -> s.getAddresses().get(0).getTargetRef().getName().equals(podName))
.map(s -> (ServiceInstance) new KubernetesServiceInstance(serviceName, s.getPorts().iterator().next().getName(), s, false))
.map(s -> (ServiceInstance) new KubernetesServiceInstance(serviceName,
s.getAddresses().stream().findFirst().orElseThrow(IllegalStateException::new),
s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new),
false))
.findFirst().orElse(defaultInstance);
} catch (Throwable t) {
return defaultInstance;
@@ -79,7 +82,8 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
Assert.notNull(serviceId, "[Assertion failed] - the object argument must be null");
return Optional.ofNullable(client.endpoints().withName(serviceId).get()).orElse(new Endpoints())
.getSubsets()
.stream().map(s -> new KubernetesServiceInstance(serviceId, s.getPorts().iterator().next().getName(), s, false))
.stream()
.flatMap(s -> s.getAddresses().stream().map(a -> (ServiceInstance) new KubernetesServiceInstance(serviceId, a ,s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new), false)))
.collect(Collectors.toList());
}

View File

@@ -16,14 +16,13 @@
package io.fabric8.spring.cloud.discovery;
import io.fabric8.kubernetes.api.model.EndpointAddress;
import io.fabric8.kubernetes.api.model.EndpointPort;
import io.fabric8.kubernetes.api.model.EndpointSubset;
import org.springframework.cloud.client.ServiceInstance;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static io.fabric8.kubernetes.client.utils.Utils.isNotNullOrEmpty;
@@ -36,14 +35,14 @@ public class KubernetesServiceInstance implements ServiceInstance {
private static final String COLN = ":";
private final String serviceId;
private final String portName;
private final EndpointSubset subset;
private final EndpointAddress endpointAddress;
private final EndpointPort endpointPort;
private final Boolean secure;
public KubernetesServiceInstance(String serviceId, String portName, EndpointSubset subset, Boolean secure) {
public KubernetesServiceInstance(String serviceId, EndpointAddress endpointAddress, EndpointPort endpointPort, Boolean secure) {
this.serviceId = serviceId;
this.portName = portName;
this.subset = subset;
this.endpointAddress = endpointAddress;
this.endpointPort = endpointPort;
this.secure = secure;
}
@@ -54,26 +53,12 @@ public class KubernetesServiceInstance implements ServiceInstance {
@Override
public String getHost() {
if (subset.getAddresses().isEmpty()) {
throw new IllegalStateException("Endpoint subset has no addresses.");
}
return subset.getAddresses().get(0).getIp();
return endpointAddress.getIp();
}
@Override
public int getPort() {
if (subset.getPorts().isEmpty()) {
throw new IllegalStateException("Endpoint subset has no ports.");
} else if (isNullOrEmpty(portName) && subset.getPorts().size() == 1) {
return subset.getPorts().get(0).getPort();
} else if (isNotNullOrEmpty(portName)) {
for (EndpointPort port : subset.getPorts()) {
if (portName.endsWith(port.getName())) {
return port.getPort();
}
}
}
throw new IllegalStateException("Endpoint subset has no matching ports.");
return endpointPort.getPort();
}
@Override

View File

@@ -0,0 +1,84 @@
package io.fabric8.spring.cloud.discovery
import io.fabric8.kubernetes.api.model.EndpointsBuilder
import io.fabric8.kubernetes.client.Config
import io.fabric8.kubernetes.client.KubernetesClient
import io.fabric8.kubernetes.server.mock.KubernetesMockServer
import org.springframework.cloud.client.ServiceInstance
import org.springframework.cloud.client.discovery.DiscoveryClient
import spock.lang.Specification
class KubernetesDiscoveryClientTest extends Specification {
private static KubernetesMockServer mockServer = new KubernetesMockServer()
private static KubernetesClient mockClient
def setupSpec() {
mockServer.init()
mockClient = mockServer.createClient()
//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")
}
def cleanupSpec() {
mockServer.destroy();
}
def "Should be able to handle endpoints single address"() {
given:
mockServer.expect().get().withPath("/api/v1/namespaces/test/endpoints/endpoint").andReturn(200, new EndpointsBuilder()
.withNewMetadata()
.withName("endpoint")
.endMetadata()
.addNewSubset()
.addNewAddress()
.withIp("ip1")
.endAddress()
.addNewPort("http",80,"TCP")
.endSubset()
.build()).once()
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient, new KubernetesDiscoveryProperties())
when:
List<ServiceInstance> instances = discoveryClient.getInstances("endpoint")
then:
instances != null
instances.size() == 1
instances.find({s -> s.host == "ip1"})
}
def "Should be able to handle endpoints multiple addresses"() {
given:
mockServer.expect().get().withPath("/api/v1/namespaces/test/endpoints/endpoint").andReturn(200, new EndpointsBuilder()
.withNewMetadata()
.withName("endpoint")
.endMetadata()
.addNewSubset()
.addNewAddress()
.withIp("ip1")
.endAddress()
.addNewAddress()
.withIp("ip2")
.endAddress()
.addNewPort("http",80,"TCP")
.endSubset()
.build()).once()
DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient, new KubernetesDiscoveryProperties())
when:
List<ServiceInstance> instances = discoveryClient.getInstances("endpoint")
then:
instances != null
instances.size() == 2
instances.find({s -> s.host == "ip1"})
instances.find({s -> s.host == "ip2"})
}
}

View File

@@ -72,7 +72,7 @@ public class ZipkinKubernetesAutoConfiguration {
.orElse(new Endpoints())
.getSubsets()
.stream()
.map(s -> new KubernetesServiceInstance(name, s.getPorts().iterator().next().getName(), s, false))
.flatMap(s -> s.getAddresses().stream().map(a -> (ServiceInstance) new KubernetesServiceInstance(name, a ,s.getPorts().stream().findFirst().orElseThrow(IllegalStateException::new), false)))
.collect(Collectors.toList());
}