Skip to content

Commit 4e977ad

Browse files
committed
Support Bound Route Service Reporting
This commit adds support for displaying bound services when listing routes at the operations layer, and makes some incorrectly required fields optional. [resolves #850]
1 parent c76a353 commit 4e977ad

File tree

4 files changed

+176
-15
lines changed

4 files changed

+176
-15
lines changed

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/routes/DefaultRoutes.java

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@
3232
import org.cloudfoundry.client.v2.routes.RouteEntity;
3333
import org.cloudfoundry.client.v2.routes.RouteExistsRequest;
3434
import org.cloudfoundry.client.v2.routes.RouteResource;
35+
import org.cloudfoundry.client.v2.serviceinstances.UnionServiceInstanceResource;
3536
import org.cloudfoundry.client.v2.shareddomains.ListSharedDomainsRequest;
3637
import org.cloudfoundry.client.v2.shareddomains.SharedDomainResource;
3738
import org.cloudfoundry.client.v2.spaces.ListSpaceApplicationsRequest;
3839
import org.cloudfoundry.client.v2.spaces.ListSpaceRoutesRequest;
40+
import org.cloudfoundry.client.v2.spaces.ListSpaceServiceInstancesRequest;
3941
import org.cloudfoundry.client.v2.spaces.SpaceResource;
4042
import org.cloudfoundry.operations.util.OperationsLogging;
4143
import org.cloudfoundry.util.ExceptionUtils;
@@ -154,13 +156,14 @@ public Flux<Route> list(ListRoutesRequest request) {
154156
getAllSpaces(cloudFoundryClient, organizationId)
155157
)))
156158
.flatMapMany(function((cloudFoundryClient, domains, spaces) -> getRoutes(cloudFoundryClient, request, this.organizationId, this.spaceId)
157-
.map(resource -> Tuples.of(cloudFoundryClient, domains, resource, spaces))))
158-
.flatMap(function((cloudFoundryClient, domains, resource, spaces) -> Mono
159+
.map(route -> Tuples.of(cloudFoundryClient, domains, route, spaces))))
160+
.flatMap(function((cloudFoundryClient, domains, route, spaces) -> Mono
159161
.when(
160-
getApplicationNames(cloudFoundryClient, ResourceUtils.getId(resource)),
161-
getDomainName(domains, ResourceUtils.getEntity(resource).getDomainId()),
162-
Mono.just(resource),
163-
getSpaceName(spaces, ResourceUtils.getEntity(resource).getSpaceId())
162+
getApplicationNames(cloudFoundryClient, ResourceUtils.getId(route)),
163+
getDomainName(domains, ResourceUtils.getEntity(route).getDomainId()),
164+
Mono.just(route),
165+
getServiceName(cloudFoundryClient, ResourceUtils.getEntity(route)),
166+
getSpaceName(spaces, ResourceUtils.getEntity(route).getSpaceId())
164167
)))
165168
.map(function(DefaultRoutes::toRoute))
166169
.transform(OperationsLogging.log("List Routes"))
@@ -312,6 +315,19 @@ private static Flux<RouteResource> getRoutes(CloudFoundryClient cloudFoundryClie
312315
}
313316
}
314317

318+
private static Mono<Optional<String>> getServiceInstanceName(CloudFoundryClient cloudFoundryClient, String serviceInstanceId, String spaceId) {
319+
return requestListSpaceServiceInstances(cloudFoundryClient, spaceId)
320+
.filter(resource -> serviceInstanceId.equals(ResourceUtils.getId(resource)))
321+
.single()
322+
.map(resource -> Optional.of(ResourceUtils.getEntity(resource).getName()));
323+
}
324+
325+
private static Mono<Optional<String>> getServiceName(CloudFoundryClient cloudFoundryClient, RouteEntity route) {
326+
return Mono.justOrEmpty(route.getServiceInstanceId())
327+
.then(serviceInstanceId -> getServiceInstanceName(cloudFoundryClient, serviceInstanceId, route.getSpaceId()))
328+
.defaultIfEmpty(Optional.empty());
329+
}
330+
315331
private static Mono<SpaceResource> getSpace(CloudFoundryClient cloudFoundryClient, String organizationId, String space) {
316332
return requestSpaces(cloudFoundryClient, organizationId, space)
317333
.single()
@@ -420,6 +436,16 @@ private static Mono<DeleteRouteResponse> requestDeleteRoute(CloudFoundryClient c
420436
.build());
421437
}
422438

439+
private static Flux<UnionServiceInstanceResource> requestListSpaceServiceInstances(CloudFoundryClient cloudFoundryClient, String spaceId) {
440+
return PaginationUtils
441+
.requestClientV2Resources(page -> cloudFoundryClient.spaces()
442+
.listServiceInstances(ListSpaceServiceInstancesRequest.builder()
443+
.page(page)
444+
.returnUserProvidedServiceInstances(true)
445+
.spaceId(spaceId)
446+
.build()));
447+
}
448+
423449
private static Flux<PrivateDomainResource> requestPrivateDomains(CloudFoundryClient cloudFoundryClient, String organizationId, String domain) {
424450
return PaginationUtils
425451
.requestClientV2Resources(page -> cloudFoundryClient.organizations()
@@ -495,17 +521,19 @@ private static Flux<SpaceResource> requestSpaces(CloudFoundryClient cloudFoundry
495521
.build()));
496522
}
497523

498-
private static Route toRoute(List<String> applications, String domain, RouteResource resource, String space) {
524+
private static Route toRoute(List<String> applications, String domain, RouteResource resource, Optional<String> service, String space) {
499525
RouteEntity entity = ResourceUtils.getEntity(resource);
500-
501-
return Route.builder()
526+
Route.Builder builder = Route.builder()
502527
.applications(applications)
503528
.domain(domain)
504529
.host(entity.getHost())
505530
.id(ResourceUtils.getId(resource))
506531
.path(entity.getPath())
507-
.space(space)
508-
.build();
532+
.space(space);
533+
534+
service.ifPresent(builder::service);
535+
536+
return builder.build();
509537
}
510538

511539
private boolean isRouteOrphan(RouteEntity entity) {

cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/routes/_Route.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616

1717
package org.cloudfoundry.operations.routes;
1818

19+
import org.cloudfoundry.Nullable;
1920
import org.immutables.value.Value;
2021

2122
import java.util.List;
2223

2324
/**
24-
* A route and the applications which are bound to the route.
25+
* A route and the applications that are bound to the route.
2526
*/
2627
@Value.Immutable
2728
abstract class _Route {
@@ -49,10 +50,30 @@ abstract class _Route {
4950
/**
5051
* The path of this route
5152
*/
53+
@Nullable
5254
abstract String getPath();
5355

56+
/**
57+
* The port of this route
58+
*/
59+
@Nullable
60+
abstract String getPort();
61+
62+
/**
63+
* The service of this route
64+
*/
65+
@Nullable
66+
abstract String getService();
67+
5468
/**
5569
* The name of the space of this route
5670
*/
5771
abstract String getSpace();
72+
73+
/**
74+
* The type of this route
75+
*/
76+
@Nullable
77+
abstract String getType();
78+
5879
}

cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/routes/DefaultRoutesTest.java

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,20 @@
4040
import org.cloudfoundry.client.v2.routes.RouteEntity;
4141
import org.cloudfoundry.client.v2.routes.RouteExistsRequest;
4242
import org.cloudfoundry.client.v2.routes.RouteResource;
43+
import org.cloudfoundry.client.v2.serviceinstances.GetServiceInstanceRequest;
44+
import org.cloudfoundry.client.v2.serviceinstances.GetServiceInstanceResponse;
45+
import org.cloudfoundry.client.v2.serviceinstances.ServiceInstanceResource;
46+
import org.cloudfoundry.client.v2.serviceinstances.UnionServiceInstanceEntity;
47+
import org.cloudfoundry.client.v2.serviceinstances.UnionServiceInstanceResource;
4348
import org.cloudfoundry.client.v2.shareddomains.ListSharedDomainsRequest;
4449
import org.cloudfoundry.client.v2.shareddomains.ListSharedDomainsResponse;
4550
import org.cloudfoundry.client.v2.shareddomains.SharedDomainResource;
4651
import org.cloudfoundry.client.v2.spaces.ListSpaceApplicationsRequest;
4752
import org.cloudfoundry.client.v2.spaces.ListSpaceApplicationsResponse;
4853
import org.cloudfoundry.client.v2.spaces.ListSpaceRoutesRequest;
4954
import org.cloudfoundry.client.v2.spaces.ListSpaceRoutesResponse;
55+
import org.cloudfoundry.client.v2.spaces.ListSpaceServiceInstancesRequest;
56+
import org.cloudfoundry.client.v2.spaces.ListSpaceServiceInstancesResponse;
5057
import org.cloudfoundry.client.v2.spaces.SpaceEntity;
5158
import org.cloudfoundry.client.v2.spaces.SpaceResource;
5259
import org.cloudfoundry.operations.AbstractOperationsTest;
@@ -444,19 +451,21 @@ public void listCurrentOrganizationNoSpace() {
444451
requestPrivateDomainsAll(this.cloudFoundryClient, TEST_ORGANIZATION_ID);
445452
requestSharedDomainsAll(this.cloudFoundryClient);
446453
requestSpacesAll(this.cloudFoundryClient, TEST_ORGANIZATION_ID);
454+
requestSpaceServiceInstances(this.cloudFoundryClient, "test-route-entity-serviceInstanceId", "test-route-entity-spaceId");
447455
requestApplications(this.cloudFoundryClient, "test-id");
448456

449457
this.routes
450458
.list(ListRoutesRequest.builder()
451459
.level(Level.ORGANIZATION)
452460
.build())
453461
.as(StepVerifier::create)
454-
.expectNext(fill(Route.builder())
462+
.expectNext(Route.builder()
455463
.application("test-application-name")
456464
.domain("test-shared-domain-name")
457465
.host("test-route-entity-host")
458466
.id("test-id")
459467
.path("test-route-entity-path")
468+
.service("test-service-instance-entityname")
460469
.space("test-space-entity-name")
461470
.build())
462471
.expectComplete()
@@ -493,7 +502,7 @@ public void listCurrentSpace() {
493502
.level(Level.SPACE)
494503
.build())
495504
.as(StepVerifier::create)
496-
.expectNext(fill(Route.builder())
505+
.expectNext(Route.builder()
497506
.application("test-application-name")
498507
.domain("test-shared-domain-name")
499508
.host("test-route-entity-host")
@@ -505,6 +514,30 @@ public void listCurrentSpace() {
505514
.verify(Duration.ofSeconds(5));
506515
}
507516

517+
@Test
518+
public void listCurrentSpaceNoPath() {
519+
requestSpaceRoutesNoPath(this.cloudFoundryClient, TEST_SPACE_ID);
520+
requestPrivateDomainsAll(this.cloudFoundryClient, TEST_ORGANIZATION_ID);
521+
requestSharedDomainsAll(this.cloudFoundryClient);
522+
requestSpacesAll(this.cloudFoundryClient, TEST_ORGANIZATION_ID);
523+
requestApplications(this.cloudFoundryClient, "test-route-id");
524+
525+
this.routes
526+
.list(ListRoutesRequest.builder()
527+
.level(Level.SPACE)
528+
.build())
529+
.as(StepVerifier::create)
530+
.expectNext(Route.builder()
531+
.application("test-application-name")
532+
.domain("test-shared-domain-name")
533+
.host("test-route-entity-host")
534+
.id("test-route-id")
535+
.space("test-space-entity-name")
536+
.build())
537+
.expectComplete()
538+
.verify(Duration.ofSeconds(5));
539+
}
540+
508541
@Test
509542
public void mapRouteAssignedPort() {
510543
requestApplications(this.cloudFoundryClient, "test-application-name", TEST_SPACE_ID);
@@ -1152,6 +1185,24 @@ private static void requestSpaceRoutesEmpty(CloudFoundryClient cloudFoundryClien
11521185
.build()));
11531186
}
11541187

1188+
private static void requestSpaceRoutesNoPath(CloudFoundryClient cloudFoundryClient, String spaceId) {
1189+
when(cloudFoundryClient.spaces()
1190+
.listRoutes(ListSpaceRoutesRequest.builder()
1191+
.page(1)
1192+
.spaceId(spaceId)
1193+
.build()))
1194+
.thenReturn(Mono
1195+
.just(fill(ListSpaceRoutesResponse.builder())
1196+
.resource(fill(RouteResource.builder(), "route-")
1197+
.entity(fill(RouteEntity.builder(), "route-entity-")
1198+
.domainId("test-domain-id")
1199+
.path(null)
1200+
.serviceInstanceId(null)
1201+
.build())
1202+
.build())
1203+
.build()));
1204+
}
1205+
11551206
private static void requestSpaceRoutesService(CloudFoundryClient cloudFoundryClient, String spaceId) {
11561207
when(cloudFoundryClient.spaces()
11571208
.listRoutes(ListSpaceRoutesRequest.builder()
@@ -1169,6 +1220,26 @@ private static void requestSpaceRoutesService(CloudFoundryClient cloudFoundryCli
11691220
.build()));
11701221
}
11711222

1223+
private static void requestSpaceServiceInstances(CloudFoundryClient cloudFoundryClient, String serviceInstanceId, String spaceId) {
1224+
when(cloudFoundryClient.spaces()
1225+
.listServiceInstances(ListSpaceServiceInstancesRequest.builder()
1226+
.page(1)
1227+
.returnUserProvidedServiceInstances(true)
1228+
.spaceId(spaceId)
1229+
.build()))
1230+
.thenReturn(Mono
1231+
.just(fill(ListSpaceServiceInstancesResponse.builder())
1232+
.resource(fill(UnionServiceInstanceResource.builder(), "service-instance-")
1233+
.metadata(fill(Metadata.builder(), "service-instance-metadata-")
1234+
.id(serviceInstanceId)
1235+
.build())
1236+
.entity(fill(UnionServiceInstanceEntity.builder(), "service-instance-entity")
1237+
.spaceId(spaceId)
1238+
.build())
1239+
.build())
1240+
.build()));
1241+
}
1242+
11721243
private static void requestSpaces(CloudFoundryClient cloudFoundryClient, String organizationId, String space) {
11731244
when(cloudFoundryClient.organizations()
11741245
.listSpaces(ListOrganizationSpacesRequest.builder()

integration-test/src/test/java/org/cloudfoundry/operations/RoutesTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.cloudfoundry.operations.routes.MapRouteRequest;
3030
import org.cloudfoundry.operations.routes.Route;
3131
import org.cloudfoundry.operations.routes.UnmapRouteRequest;
32+
import org.cloudfoundry.operations.services.BindRouteServiceInstanceRequest;
33+
import org.cloudfoundry.operations.services.CreateUserProvidedServiceInstanceRequest;
3234
import org.junit.Test;
3335
import org.springframework.beans.factory.annotation.Autowired;
3436
import org.springframework.core.io.ClassPathResource;
@@ -41,7 +43,6 @@
4143
import java.time.Duration;
4244
import java.util.Collections;
4345
import java.util.Optional;
44-
import java.util.concurrent.TimeoutException;
4546
import java.util.function.Predicate;
4647

4748
import static org.assertj.core.api.Assertions.assertThat;
@@ -283,6 +284,28 @@ public void listWithOrganizationLevel() {
283284
.verify(Duration.ofMinutes(5));
284285
}
285286

287+
@Test
288+
public void listWithService() {
289+
String domainName = this.nameFactory.getDomainName();
290+
String hostName = this.nameFactory.getHostName();
291+
String path = this.nameFactory.getPath();
292+
String serviceInstanceName = this.nameFactory.getServiceInstanceName();
293+
294+
createSharedDomainAndRoute(this.cloudFoundryOperations, this.spaceName, domainName, hostName, path)
295+
.then(requestCreateUserProvidedServiceInstance(this.cloudFoundryOperations, serviceInstanceName))
296+
.then(requestBindRouteServiceInstance(this.cloudFoundryOperations, domainName, hostName, path, serviceInstanceName))
297+
.thenMany(this.cloudFoundryOperations.routes()
298+
.list(ListRoutesRequest.builder()
299+
.level(SPACE)
300+
.build()))
301+
.filter(filterRoutes(domainName, hostName, path, null))
302+
.map(Route::getService)
303+
.as(StepVerifier::create)
304+
.expectNext(serviceInstanceName)
305+
.expectComplete()
306+
.verify(Duration.ofMinutes(5));
307+
}
308+
286309
@Test
287310
public void listWithSpaceLevel() {
288311
String domainName = this.nameFactory.getDomainName();
@@ -539,6 +562,16 @@ private static Predicate<Route> filterRoutes(String domainName, String host, Str
539562
&& Optional.ofNullable(path).map(route.getPath()::equals).orElse(true);
540563
}
541564

565+
private static Mono<Void> requestBindRouteServiceInstance(CloudFoundryOperations cloudFoundryOperations, String domainName, String hostName, String path, String serviceInstanceName) {
566+
return cloudFoundryOperations.services()
567+
.bindRoute(BindRouteServiceInstanceRequest.builder()
568+
.domainName(domainName)
569+
.hostname(hostName)
570+
.path(path)
571+
.serviceInstanceName(serviceInstanceName)
572+
.build());
573+
}
574+
542575
private static Mono<Void> requestCreateApplication(CloudFoundryOperations cloudFoundryOperations, Path application, String name, Boolean noStart) {
543576
return cloudFoundryOperations.applications()
544577
.push(PushApplicationRequest.builder()
@@ -596,6 +629,14 @@ private static Mono<Void> requestCreateSharedDomain(CloudFoundryOperations cloud
596629
.build());
597630
}
598631

632+
private static Mono<Void> requestCreateUserProvidedServiceInstance(CloudFoundryOperations cloudFoundryOperations, String name) {
633+
return cloudFoundryOperations.services()
634+
.createUserProvidedInstance(CreateUserProvidedServiceInstanceRequest.builder()
635+
.name(name)
636+
.routeServiceUrl("https://test.route.service")
637+
.build());
638+
}
639+
599640
private static Flux<Route> requestListRoutes(CloudFoundryOperations cloudFoundryOperations) {
600641
return cloudFoundryOperations.routes()
601642
.list(ListRoutesRequest.builder()

0 commit comments

Comments
 (0)