diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java index da677508..9c1156d5 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocator.java @@ -96,8 +96,11 @@ public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLoc }; } - return serviceInstances.filter(instances -> !instances.isEmpty()).map(instances -> instances.get(0)) - .filter(includePredicate).map(instance -> { + return serviceInstances.filter(instances -> !instances.isEmpty()) + .flatMap(Flux::fromIterable).filter(includePredicate).collectMap(ServiceInstance::getServiceId) + // remove duplicates + .flatMapMany(map -> Flux.fromIterable(map.values())) + .map(instance -> { RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance); final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java index da1e5617..6116055a 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/actuate/GatewayControllerEndpointTests.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.function.Predicate; import org.assertj.core.util.Maps; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -44,7 +45,9 @@ import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.cloud.gateway.test.PermitAllSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.BodyInserters; @@ -117,6 +120,26 @@ public class GatewayControllerEndpointTests { }); } + @Test + public void testRouteDelete() { + RouteDefinition testRouteDefinition = new RouteDefinition(); + testRouteDefinition.setUri(URI.create("http://example.org")); + + PredicateDefinition methodRoutePredicateDefinition = new PredicateDefinition("Method=GET"); + + testRouteDefinition.setPredicates(Arrays.asList(methodRoutePredicateDefinition)); + + testClient.post().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route-to-be-delete") + .accept(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(testRouteDefinition)).exchange() + .expectStatus().isCreated(); + + testClient.delete().uri("http://localhost:" + port + "/actuator/gateway/routes/test-route-to-be-delete") + .exchange().expectStatus().isOk().expectBody(ResponseEntity.class).consumeWith(result -> { + HttpStatus httpStatus = result.getStatus(); + Assert.assertEquals(HttpStatus.OK, httpStatus); + }); + } + @Test public void testPostValidRouteDefinition() { diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java index d46ab4bd..85fda6ce 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/discovery/DiscoveryClientRouteDefinitionLocatorTests.java @@ -67,8 +67,9 @@ public class DiscoveryClientRouteDefinitionLocatorTests { public void includeExpressionWorks() { assertThat(locator).as("DiscoveryClientRouteDefinitionLocator was null").isNotNull(); - List definitions = locator.getRouteDefinitions().collectList().block(); - assertThat(definitions).hasSize(1); + List definitions = locator.getRouteDefinitions().collectList() + .block(); + assertThat(definitions).hasSize(2); RouteDefinition definition = definitions.get(0); assertThat(definition.getId()).isEqualTo("testedge_SERVICE1"); @@ -85,6 +86,22 @@ public class DiscoveryClientRouteDefinitionLocatorTests { assertThat(filter.getName()).isEqualTo("RewritePath"); assertThat(filter.getArgs()).hasSize(2).containsEntry(REGEXP_KEY, "/service1/?(?.*)") .containsEntry(REPLACEMENT_KEY, "/${remaining}"); + + RouteDefinition definition2 = definitions.get(1); + assertThat(definition2.getId()).isEqualTo("testedge_service3"); + assertThat(definition2.getUri()).hasScheme("lb").hasHost("service3"); + assertThat(definition2.getMetadata()).containsEntry("edge", "true"); + + assertThat(definition2.getPredicates()).hasSize(1); + PredicateDefinition predicate2 = definition2.getPredicates().get(0); + assertThat(predicate2.getName()).isEqualTo("Path"); + assertThat(predicate2.getArgs()).hasSize(1).containsEntry(PATTERN_KEY, "/service3/**"); + + assertThat(definition.getFilters()).hasSize(1); + FilterDefinition filter2 = definition2.getFilters().get(0); + assertThat(filter2.getName()).isEqualTo("RewritePath"); + assertThat(filter2.getArgs()).hasSize(2).containsEntry(REGEXP_KEY, "/service3/?(?.*)") + .containsEntry(REPLACEMENT_KEY, "/${remaining}"); } @SpringBootConfiguration @@ -94,17 +111,30 @@ public class DiscoveryClientRouteDefinitionLocatorTests { @Bean ReactiveDiscoveryClient discoveryClient() { ReactiveDiscoveryClient discoveryClient = mock(ReactiveDiscoveryClient.class); - when(discoveryClient.getServices()).thenReturn(Flux.just("SERVICE1", "Service2")); + when(discoveryClient.getServices()).thenReturn(Flux.just("SERVICE1", "Service2", "service3")); whenInstance(discoveryClient, "SERVICE1", Collections.singletonMap("edge", "true")); whenInstance(discoveryClient, "Service2", Collections.emptyMap()); + whenInstance(discoveryClient, "service3", Collections.singletonMap("edge", "true")); return discoveryClient; } private void whenInstance(ReactiveDiscoveryClient discoveryClient, String serviceId, Map metadata) { - DefaultServiceInstance instance1 = new DefaultServiceInstance(serviceId + "8001", serviceId, "localhost", - 8001, false, metadata); - when(discoveryClient.getInstances(serviceId)).thenReturn(Flux.just(instance1)); + if ("SERVICE1".equals(serviceId) || "Service2".equals(serviceId)) { + DefaultServiceInstance instance1 = new DefaultServiceInstance(serviceId + "8001", serviceId, + "localhost", 8001, false, metadata); + when(discoveryClient.getInstances(serviceId)).thenReturn(Flux.just(instance1)); + } + else if ("service3".equalsIgnoreCase(serviceId)){ + // given 3 instances, only one routedefinition should be created + DefaultServiceInstance instance2 = new DefaultServiceInstance(serviceId + "8002", serviceId, + "localhost", 8002, false, Collections.emptyMap()); + DefaultServiceInstance instance3 = new DefaultServiceInstance(serviceId + "8003", serviceId, + "localhost", 8003, false, metadata); + DefaultServiceInstance instance4 = new DefaultServiceInstance(serviceId + "8004", serviceId, + "localhost", 8004, false, metadata); + when(discoveryClient.getInstances(serviceId)).thenReturn(Flux.just(instance2, instance3, instance4)); + } } }