Merge pull request #466 from Haybu/bug/issue-465

service (for its metadata) should be queried per namespace. fixes gh-465
This commit is contained in:
Haytham Mohamed
2019-09-16 10:40:39 -05:00
committed by GitHub
4 changed files with 150 additions and 54 deletions

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2019 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.discovery;
import java.util.ArrayList;
import java.util.List;
import io.fabric8.kubernetes.api.model.EndpointSubset;
/**
* @author Haytham Mohamed
**/
public class EndpointSubsetNS {
private String namespace;
private List<EndpointSubset> endpointSubset;
public EndpointSubsetNS() {
endpointSubset = new ArrayList<>();
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public List<EndpointSubset> getEndpointSubset() {
return endpointSubset;
}
public void setEndpointSubset(List<EndpointSubset> endpointSubset) {
this.endpointSubset = endpointSubset;
}
public boolean equals(Object o) {
return this.endpointSubset.equals(o);
}
public int hashCode() {
return this.endpointSubset.hashCode();
}
}

View File

@@ -101,40 +101,38 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
public List<ServiceInstance> getInstances(String serviceId) {
Assert.notNull(serviceId,
"[Assertion failed] - the object argument must not be null");
List<Endpoints> endpointsList = this.properties.isAllNamespaces()
? this.client.endpoints().inAnyNamespace()
.withField("metadata.name", serviceId).list().getItems()
: Collections
.singletonList(this.client.endpoints().withName(serviceId).get());
List<EndpointSubset> subsets = endpointsList.stream()
.flatMap(endpoints -> getSubsetsFromEndpoints(endpoints).stream())
List<EndpointSubsetNS> subsetsNS = endpointsList.stream()
.map(endpoints -> getSubsetsFromEndpoints(endpoints))
.collect(Collectors.toList());
List<ServiceInstance> instances = new ArrayList<>();
if (!subsetsNS.isEmpty()) {
for (EndpointSubsetNS es : subsetsNS) {
instances.addAll(this.getNamespaceServiceInstances(es, serviceId));
}
}
return instances;
}
private List<ServiceInstance> getNamespaceServiceInstances(EndpointSubsetNS es,
String serviceId) {
String namespace = es.getNamespace();
List<EndpointSubset> subsets = es.getEndpointSubset();
List<ServiceInstance> instances = new ArrayList<>();
if (!subsets.isEmpty()) {
final Service service = this.client.services().withName(serviceId).get();
final Map<String, String> serviceMetadata = new HashMap<>();
final Service service = this.client.services().inNamespace(namespace)
.withName(serviceId).get();
final Map<String, String> serviceMetadata = this.getServiceMetadata(service);
KubernetesDiscoveryProperties.Metadata metadataProps = this.properties
.getMetadata();
if (metadataProps.isAddLabels()) {
Map<String, String> labelMetadata = getMapWithPrefixedKeys(
service.getMetadata().getLabels(),
metadataProps.getLabelsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding label metadata: " + labelMetadata);
}
serviceMetadata.putAll(labelMetadata);
}
if (metadataProps.isAddAnnotations()) {
Map<String, String> annotationMetadata = getMapWithPrefixedKeys(
service.getMetadata().getAnnotations(),
metadataProps.getAnnotationsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding annotation metadata: " + annotationMetadata);
}
serviceMetadata.putAll(annotationMetadata);
}
for (EndpointSubset s : subsets) {
// Extend the service metadata map with per-endpoint port information (if
@@ -176,6 +174,31 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
return instances;
}
private Map<String, String> getServiceMetadata(Service service) {
final Map<String, String> serviceMetadata = new HashMap<>();
KubernetesDiscoveryProperties.Metadata metadataProps = this.properties
.getMetadata();
if (metadataProps.isAddLabels()) {
Map<String, String> labelMetadata = getMapWithPrefixedKeys(
service.getMetadata().getLabels(), metadataProps.getLabelsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding label metadata: " + labelMetadata);
}
serviceMetadata.putAll(labelMetadata);
}
if (metadataProps.isAddAnnotations()) {
Map<String, String> annotationMetadata = getMapWithPrefixedKeys(
service.getMetadata().getAnnotations(),
metadataProps.getAnnotationsPrefix());
if (log.isDebugEnabled()) {
log.debug("Adding annotation metadata: " + annotationMetadata);
}
serviceMetadata.putAll(annotationMetadata);
}
return serviceMetadata;
}
private EndpointPort findEndpointPort(EndpointSubset s) {
List<EndpointPort> ports = s.getPorts();
EndpointPort endpointPort;
@@ -197,15 +220,17 @@ public class KubernetesDiscoveryClient implements DiscoveryClient {
return endpointPort;
}
private List<EndpointSubset> getSubsetsFromEndpoints(Endpoints endpoints) {
if (endpoints == null) {
return new ArrayList<>();
}
if (endpoints.getSubsets() == null) {
return new ArrayList<>();
private EndpointSubsetNS getSubsetsFromEndpoints(Endpoints endpoints) {
EndpointSubsetNS es = new EndpointSubsetNS();
es.setNamespace(this.client.getNamespace()); // start with the default that comes
// with the client
if (endpoints != null && endpoints.getSubsets() != null) {
es.setNamespace(endpoints.getMetadata().getNamespace());
es.setEndpointSubset(endpoints.getSubsets());
}
return endpoints.getSubsets();
return es;
}
// returns a new map that contain all the entries of the original map

View File

@@ -27,6 +27,7 @@ import io.fabric8.kubernetes.api.model.EndpointPortBuilder;
import io.fabric8.kubernetes.api.model.Endpoints;
import io.fabric8.kubernetes.api.model.EndpointsBuilder;
import io.fabric8.kubernetes.api.model.EndpointsList;
import io.fabric8.kubernetes.api.model.ObjectMeta;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.ServiceList;
@@ -48,6 +49,7 @@ import org.springframework.cloud.client.ServiceInstance;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -89,7 +91,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "lab");
@@ -119,7 +121,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "v1");
@@ -152,7 +154,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "v1");
@@ -184,7 +186,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddAnnotations()).thenReturn(true);
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "v1");
@@ -217,7 +219,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.getAnnotationsPrefix()).thenReturn("a_");
when(this.metadata.isAddPorts()).thenReturn(false);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "v1");
@@ -249,7 +251,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddAnnotations()).thenReturn(false);
when(this.metadata.isAddPorts()).thenReturn(true);
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "v1");
@@ -281,7 +283,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddPorts()).thenReturn(true);
when(this.metadata.getPortsPrefix()).thenReturn("p_");
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "v1");
@@ -315,7 +317,7 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
when(this.metadata.isAddPorts()).thenReturn(true);
when(this.metadata.getPortsPrefix()).thenReturn("p_");
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId,
setupServiceWithLabelsAndAnnotationsAndPorts(serviceId, "ns",
new HashMap<String, String>() {
{
put("l1", "la1");
@@ -339,18 +341,24 @@ public class KubernetesDiscoveryClientFilterMetadataTest {
}
private void setupServiceWithLabelsAndAnnotationsAndPorts(String serviceId,
Map<String, String> labels, Map<String, String> annotations,
String namespace, Map<String, String> labels, Map<String, String> annotations,
Map<Integer, String> ports) {
final Service service = new ServiceBuilder().withNewMetadata().withLabels(labels)
.withAnnotations(annotations).endMetadata().withNewSpec()
.withPorts(getServicePorts(ports)).endSpec().build();
final Service service = new ServiceBuilder().withNewMetadata()
.withNamespace(namespace).withLabels(labels).withAnnotations(annotations)
.endMetadata().withNewSpec().withPorts(getServicePorts(ports)).endSpec()
.build();
when(this.serviceOperation.withName(serviceId)).thenReturn(this.serviceResource);
when(this.serviceResource.get()).thenReturn(service);
when(this.kubernetesClient.services()).thenReturn(this.serviceOperation);
when(this.kubernetesClient.services().inNamespace(anyString()))
.thenReturn(this.serviceOperation);
final Endpoints endpoints = new EndpointsBuilder().addNewSubset()
.addAllToPorts(getEndpointPorts(ports)).addNewAddress().endAddress()
.endSubset().build();
ObjectMeta objectMeta = new ObjectMeta();
objectMeta.setNamespace(namespace);
final Endpoints endpoints = new EndpointsBuilder().withMetadata(objectMeta)
.addNewSubset().addAllToPorts(getEndpointPorts(ports)).addNewAddress()
.endAddress().endSubset().build();
when(this.endpointsResource.get()).thenReturn(endpoints);
when(this.endpointsOperation.withName(serviceId))

View File

@@ -116,13 +116,14 @@ public class KubernetesDiscoveryClientTest {
.andReturn(200, services).once();
mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint")
.andReturn(200, service1).once();
.andReturn(200, service1).always();
mockServer.expect().get().withPath("/api/v1/namespaces/test2/services/endpoint")
.andReturn(200, service2).once();
.andReturn(200, service2).always();
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
properties.setAllNamespaces(true);
final DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient,
properties, KubernetesClient::services,
new DefaultIsServicePortSecureResolver(properties));
@@ -149,16 +150,17 @@ public class KubernetesDiscoveryClientTest {
.endAddress().addNewPort("http", 80, "TCP").endSubset().build())
.once();
mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint")
mockServer.expect().get().withPath("/api/v1/services/endpoint")
.andReturn(200, new ServiceBuilder().withNewMetadata()
.withName("endpoint").withLabels(new HashMap<String, String>() {
{
put("l", "v");
}
}).endMetadata().build())
.once();
.always();
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
final DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient,
properties, KubernetesClient::services,
new DefaultIsServicePortSecureResolver(properties));
@@ -180,14 +182,14 @@ public class KubernetesDiscoveryClientTest {
.addNewPort("http", 80, "TCP").endSubset().build())
.once();
mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint")
mockServer.expect().get().withPath("/api/v1/services/endpoint")
.andReturn(200, new ServiceBuilder().withNewMetadata()
.withName("endpoint").withLabels(new HashMap<String, String>() {
{
put("l", "v");
}
}).endMetadata().build())
.once();
.always();
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
properties.setPrimaryPortName("http");
@@ -212,14 +214,14 @@ public class KubernetesDiscoveryClientTest {
.endAddress().addNewPort("https", 443, "TCP").endSubset().build())
.once();
mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint")
mockServer.expect().get().withPath("/api/v1/services/endpoint")
.andReturn(200, new ServiceBuilder().withNewMetadata()
.withName("endpoint").withLabels(new HashMap<String, String>() {
{
put("l", "v");
}
}).endMetadata().build())
.once();
.always();
final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties();
final DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient,