diff --git a/docs/src/main/asciidoc/discovery-client.adoc b/docs/src/main/asciidoc/discovery-client.adoc index 2d0bead0..8b639fd0 100644 --- a/docs/src/main/asciidoc/discovery-client.adoc +++ b/docs/src/main/asciidoc/discovery-client.adoc @@ -44,6 +44,15 @@ private DiscoveryClient discoveryClient; ---- ==== +You can choose to enable `DiscoveryClient` from all namespaces by setting the following property in `application.properties`: + +==== +[source] +---- +spring.cloud.kubernetes.discovery.all-namespaces=true +---- +==== + If, for any reason, you need to disable the `DiscoveryClient`, you can set the following property in `application.properties`: ==== diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java index 3634f8b4..19db10a6 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClient.java @@ -17,6 +17,7 @@ package org.springframework.cloud.kubernetes.discovery; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -100,9 +101,14 @@ public class KubernetesDiscoveryClient implements DiscoveryClient { public List getInstances(String serviceId) { Assert.notNull(serviceId, "[Assertion failed] - the object argument must not be null"); - - Endpoints endpoints = this.client.endpoints().withName(serviceId).get(); - List subsets = getSubsetsFromEndpoints(endpoints); + List endpointsList = this.properties.isAllNamespaces() + ? this.client.endpoints().inAnyNamespace() + .withField("metadata.name", serviceId).list().getItems() + : Collections + .singletonList(this.client.endpoints().withName(serviceId).get()); + List subsets = endpointsList.stream() + .flatMap(endpoints -> getSubsetsFromEndpoints(endpoints).stream()) + .collect(Collectors.toList()); List instances = new ArrayList<>(); if (!subsets.isEmpty()) { diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfiguration.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfiguration.java index d5ebc2fa..97060f65 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfiguration.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientAutoConfiguration.java @@ -53,10 +53,23 @@ public class KubernetesDiscoveryClientAutoConfiguration { public KubernetesClientServicesFunction servicesFunction( KubernetesDiscoveryProperties properties) { if (properties.getServiceLabels().isEmpty()) { - return KubernetesClient::services; + if (properties.isAllNamespaces()) { + return (client) -> client.services().inAnyNamespace(); + } + else { + return KubernetesClient::services; + } + } + else { + if (properties.isAllNamespaces()) { + return (client) -> client.services().inAnyNamespace() + .withLabels(properties.getServiceLabels()); + } + else { + return (client) -> client.services() + .withLabels(properties.getServiceLabels()); + } } - - return (client) -> client.services().withLabels(properties.getServiceLabels()); } @Bean diff --git a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryProperties.java b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryProperties.java index 42dc4ebf..012a1ee6 100644 --- a/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryProperties.java +++ b/spring-cloud-kubernetes-discovery/src/main/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryProperties.java @@ -40,6 +40,9 @@ public class KubernetesDiscoveryProperties { @Value("${spring.application.name:unknown}") private String serviceName = "unknown"; + /** If discovering all namespaces. */ + private boolean allNamespaces = false; + /** * SpEL expression to filter services AFTER they have been retrieved from the * Kubernetes API server. @@ -124,6 +127,14 @@ public class KubernetesDiscoveryProperties { this.metadata = metadata; } + public boolean isAllNamespaces() { + return allNamespaces; + } + + public void setAllNamespaces(boolean allNamespaces) { + this.allNamespaces = allNamespaces; + } + @Override public String toString() { return new ToStringCreator(this).append("enabled", this.enabled) diff --git a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientTest.java b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientTest.java index 44389639..4dfb929c 100644 --- a/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientTest.java +++ b/spring-cloud-kubernetes-discovery/src/test/java/org/springframework/cloud/kubernetes/discovery/KubernetesDiscoveryClientTest.java @@ -16,11 +16,16 @@ package org.springframework.cloud.kubernetes.discovery; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; +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.Service; import io.fabric8.kubernetes.api.model.ServiceBuilder; +import io.fabric8.kubernetes.api.model.ServiceList; import io.fabric8.kubernetes.api.model.ServiceListBuilder; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; @@ -54,12 +59,93 @@ public class KubernetesDiscoveryClientTest { "false"); } + @Test + public void getInstancesShouldBeAbleToHandleEndpointsFromMultipleNamespaces() { + Endpoints endPoints1 = new EndpointsBuilder().withNewMetadata() + .withName("endpoint").withNamespace("test").endMetadata().addNewSubset() + .addNewAddress().withIp("ip1").withNewTargetRef().withUid("uid1") + .endTargetRef().endAddress().addNewPort("http", 80, "TCP").endSubset() + .build(); + + Endpoints endpoints2 = new EndpointsBuilder().withNewMetadata() + .withName("endpoint").withNamespace("test2").endMetadata().addNewSubset() + .addNewAddress().withIp("ip2").withNewTargetRef().withUid("uid2") + .endTargetRef().endAddress().addNewPort("http", 80, "TCP").endSubset() + .build(); + + List endpointsList = new ArrayList<>(); + endpointsList.add(endPoints1); + endpointsList.add(endpoints2); + + EndpointsList endpoints = new EndpointsList(); + endpoints.setItems(endpointsList); + + mockServer.expect().get() + .withPath("/api/v1/endpoints?fieldSelector=metadata.name%3Dendpoint") + .andReturn(200, endpoints).once(); + + mockServer.expect().get().withPath("/api/v1/namespaces/test/endpoints/endpoint") + .andReturn(200, endPoints1).once(); + + mockServer.expect().get().withPath("/api/v1/namespaces/test2/endpoints/endpoint") + .andReturn(200, endpoints2).once(); + + Service service1 = new ServiceBuilder().withNewMetadata().withName("endpoint") + .withNamespace("test").withLabels(new HashMap() { + { + put("l", "v"); + } + }).endMetadata().build(); + + Service service2 = new ServiceBuilder().withNewMetadata().withName("endpoint") + .withNamespace("test2").withLabels(new HashMap() { + { + put("l", "v"); + } + }).endMetadata().build(); + + List servicesList = new ArrayList<>(); + servicesList.add(service1); + servicesList.add(service2); + + ServiceList services = new ServiceList(); + services.setItems(servicesList); + + mockServer.expect().get() + .withPath("/api/v1/services?fieldSelector=metadata.name%3Dendpoint") + .andReturn(200, services).once(); + + mockServer.expect().get().withPath("/api/v1/namespaces/test/services/endpoint") + .andReturn(200, service1).once(); + + mockServer.expect().get().withPath("/api/v1/namespaces/test2/services/endpoint") + .andReturn(200, service2).once(); + + final KubernetesDiscoveryProperties properties = new KubernetesDiscoveryProperties(); + properties.setAllNamespaces(true); + final DiscoveryClient discoveryClient = new KubernetesDiscoveryClient(mockClient, + properties, KubernetesClient::services, + new DefaultIsServicePortSecureResolver(properties)); + + final List instances = discoveryClient.getInstances("endpoint"); + + assertThat(instances).hasSize(2); + assertThat(instances).filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()) + .hasSize(1); + assertThat(instances).filteredOn(s -> s.getHost().equals("ip2") && !s.isSecure()) + .hasSize(1); + assertThat(instances).filteredOn(s -> s.getInstanceId().equals("uid1")) + .hasSize(1); + assertThat(instances).filteredOn(s -> s.getInstanceId().equals("uid2")) + .hasSize(1); + } + @Test public void getInstancesShouldBeAbleToHandleEndpointsSingleAddress() { mockServer.expect().get().withPath("/api/v1/namespaces/test/endpoints/endpoint") .andReturn(200, new EndpointsBuilder().withNewMetadata() .withName("endpoint").endMetadata().addNewSubset().addNewAddress() - .withIp("ip1").withNewTargetRef().withUid("uid").endTargetRef() + .withIp("ip1").withNewTargetRef().withUid("uid1").endTargetRef() .endAddress().addNewPort("http", 80, "TCP").endSubset().build()) .once(); @@ -81,7 +167,7 @@ public class KubernetesDiscoveryClientTest { assertThat(instances).hasSize(1) .filteredOn(s -> s.getHost().equals("ip1") && !s.isSecure()).hasSize(1) - .filteredOn(s -> s.getInstanceId().equals("uid")).hasSize(1); + .filteredOn(s -> s.getInstanceId().equals("uid1")).hasSize(1); } @Test