diff --git a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/routes/DefaultRoutes.java b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/routes/DefaultRoutes.java index 0bd0de2357..776ac8cfcd 100644 --- a/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/routes/DefaultRoutes.java +++ b/cloudfoundry-operations/src/main/java/org/cloudfoundry/operations/routes/DefaultRoutes.java @@ -1,23 +1,20 @@ /* * Copyright 2013-2021 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 + * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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. + * 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.cloudfoundry.operations.routes; import static org.cloudfoundry.util.tuple.TupleUtils.function; -import static org.cloudfoundry.util.tuple.TupleUtils.predicate; import java.time.Duration; import java.util.Collections; @@ -38,10 +35,8 @@ import org.cloudfoundry.client.v2.privatedomains.PrivateDomainResource; import org.cloudfoundry.client.v2.routes.AbstractRouteResource; import org.cloudfoundry.client.v2.routes.CreateRouteResponse; -import org.cloudfoundry.client.v2.routes.DeleteRouteResponse; import org.cloudfoundry.client.v2.routes.ListRouteApplicationsRequest; import org.cloudfoundry.client.v2.routes.RouteEntity; -import org.cloudfoundry.client.v2.routes.RouteExistsRequest; import org.cloudfoundry.client.v2.routes.RouteResource; import org.cloudfoundry.client.v2.serviceinstances.UnionServiceInstanceResource; import org.cloudfoundry.client.v2.shareddomains.ListSharedDomainsRequest; @@ -50,6 +45,10 @@ import org.cloudfoundry.client.v2.spaces.ListSpaceRoutesRequest; import org.cloudfoundry.client.v2.spaces.ListSpaceServiceInstancesRequest; import org.cloudfoundry.client.v2.spaces.SpaceResource; +import org.cloudfoundry.client.v3.domains.CheckReservedRoutesRequest; +import org.cloudfoundry.client.v3.domains.DomainResource; +import org.cloudfoundry.client.v3.organizations.ListOrganizationDomainsRequest; +import org.cloudfoundry.client.v3.spaces.DeleteUnmappedRoutesRequest; import org.cloudfoundry.operations.util.OperationsLogging; import org.cloudfoundry.util.ExceptionUtils; import org.cloudfoundry.util.JobUtils; @@ -81,18 +80,18 @@ public Mono check(CheckRouteRequest request) { return Mono.zip(this.cloudFoundryClient, this.organizationId) .flatMap( function( - (cloudFoundryClient, organizationId) -> + (client, organizationId) -> Mono.zip( - Mono.just(cloudFoundryClient), - getOptionalDomainId( - cloudFoundryClient, + this.cloudFoundryClient, + getOptionalDomainIdByName( + client, organizationId, request.getDomain())))) .flatMap( function( - (cloudFoundryClient, domainId) -> - requestRouteExists( - cloudFoundryClient, + (client, domainId) -> + routeExists( + client, domainId, request.getHost(), request.getPath()))) @@ -101,6 +100,42 @@ public Mono check(CheckRouteRequest request) { .checkpoint(); } + private static Mono routeExists( + CloudFoundryClient cloudFoundryClient, String domainId, String host, String path) { + return cloudFoundryClient + .domainsV3() + .checkReservedRoutes( + CheckReservedRoutesRequest.builder() + .domainId(domainId) + .host(host) + .path(path) + .build()) + .flatMap(response -> Mono.just(response.getMatchingRoute())); + } + + private static Mono getOptionalDomainIdByName( + CloudFoundryClient cloudFoundryClient, String organizationId, String domain) { + return listDomains(cloudFoundryClient, organizationId, new String[] {domain}) + .singleOrEmpty() + .map(resource -> resource.getId()); + } + + private static Flux listDomains( + CloudFoundryClient cloudFoundryClient, + String organizationId, + String[] domainNamesFilter) { + return PaginationUtils.requestClientV3Resources( + page -> + cloudFoundryClient + .organizationsV3() + .listDomains( + ListOrganizationDomainsRequest.builder() + .names(domainNamesFilter) + .page(page) + .organizationId(organizationId) + .build())); + } + @Override public Mono create(CreateRouteRequest request) { return Mono.zip(this.cloudFoundryClient, this.organizationId) @@ -170,43 +205,22 @@ public Mono delete(DeleteRouteRequest request) { @Override public Mono deleteOrphanedRoutes(DeleteOrphanedRoutesRequest request) { return Mono.zip(this.cloudFoundryClient, this.spaceId) - .flatMapMany( - function( - (cloudFoundryClient, spaceId) -> - requestSpaceRoutes(cloudFoundryClient, spaceId) - .filter( - route -> - isRouteOrphan( - ResourceUtils.getEntity( - route))) - .map(ResourceUtils::getId) - .map( - routeId -> - Tuples.of( - cloudFoundryClient, - routeId)))) .flatMap( function( - (cloudFoundryClient, routeId) -> - getApplications(cloudFoundryClient, routeId) - .map( - applicationResources -> - Tuples.of( - cloudFoundryClient, - applicationResources, - routeId)))) - .filter( - predicate( - (cloudFoundryClient, applicationResources, routeId) -> - isApplicationOrphan(applicationResources))) + (client, spaceId) -> + Mono.zip( + this.cloudFoundryClient, + client.spacesV3() + .deleteUnmappedRoutes( + DeleteUnmappedRoutesRequest + .builder() + .spaceId(spaceId) + .build())))) .flatMap( function( - (cloudFoundryClient, applicationResources, routeId) -> - deleteRoute( - cloudFoundryClient, - request.getCompletionTimeout(), - routeId))) - .then() + (client, job) -> + JobUtils.waitForCompletion( + client, request.getCompletionTimeout(), job))) .transform(OperationsLogging.log("Delete Orphaned Routes")) .checkpoint(); } @@ -324,15 +338,6 @@ public Mono unmap(UnmapRouteRequest request) { .checkpoint(); } - private static Mono deleteRoute( - CloudFoundryClient cloudFoundryClient, Duration completionTimeout, String routeId) { - return requestDeleteRoute(cloudFoundryClient, routeId) - .flatMap( - job -> - JobUtils.waitForCompletion( - cloudFoundryClient, completionTimeout, job)); - } - private static Mono> getAllDomains( CloudFoundryClient cloudFoundryClient, String organizationId) { return requestAllPrivateDomains(cloudFoundryClient, organizationId) @@ -386,11 +391,6 @@ private static Mono> getApplicationNames( .collectList(); } - private static Mono> getApplications( - CloudFoundryClient cloudFoundryClient, String routeId) { - return requestApplications(cloudFoundryClient, routeId).collectList(); - } - private static Mono> getDomain( CloudFoundryClient cloudFoundryClient, String organizationId, String domain) { return getDomains(cloudFoundryClient, organizationId, domain) @@ -416,13 +416,6 @@ private static Flux> getDomains( .switchIfEmpty(requestSharedDomains(cloudFoundryClient, domain)); } - private static Mono getOptionalDomainId( - CloudFoundryClient cloudFoundryClient, String organizationId, String domain) { - return getDomains(cloudFoundryClient, organizationId, domain) - .singleOrEmpty() - .map(ResourceUtils::getId); - } - private static Mono getOrCreateRoute( CloudFoundryClient cloudFoundryClient, String organizationId, @@ -562,10 +555,6 @@ private static Mono getSpaceName(Map spaces, String spac return Mono.just(spaces.get(spaceId)); } - private static boolean isApplicationOrphan(List applications) { - return applications.isEmpty(); - } - private static boolean isIdentical(String s, String t) { return s == null ? t == null : s.equals(t); } @@ -673,13 +662,21 @@ private static Mono requestCreateRoute( .create(builder.domainId(domainId).spaceId(spaceId).build()); } - private static Mono requestDeleteRoute( + private static Mono deleteRoute( + CloudFoundryClient cloudFoundryClient, Duration completionTimeout, String routeId) { + return requestDeleteRoute(cloudFoundryClient, routeId) + .flatMap( + job -> + JobUtils.waitForCompletion( + cloudFoundryClient, completionTimeout, job)); + } + + private static Mono requestDeleteRoute( CloudFoundryClient cloudFoundryClient, String routeId) { return cloudFoundryClient - .routes() + .routesV3() .delete( - org.cloudfoundry.client.v2.routes.DeleteRouteRequest.builder() - .async(true) + org.cloudfoundry.client.v3.routes.DeleteRouteRequest.builder() .routeId(routeId) .build()); } @@ -723,18 +720,6 @@ private static Mono requestRemoveRouteFromApplication( .build()); } - private static Mono requestRouteExists( - CloudFoundryClient cloudFoundryClient, String domainId, String host, String path) { - return cloudFoundryClient - .routes() - .exists( - RouteExistsRequest.builder() - .domainId(domainId) - .host(host) - .path(path) - .build()); - } - private static Flux requestRoutes( CloudFoundryClient cloudFoundryClient, UnaryOperator modifier) { @@ -827,8 +812,4 @@ private static Route toRoute( return builder.build(); } - - private boolean isRouteOrphan(RouteEntity entity) { - return entity.getServiceInstanceId() == null || entity.getServiceInstanceId().isEmpty(); - } } diff --git a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java index c5afec5a21..ff43740846 100644 --- a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java +++ b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/AbstractOperationsTest.java @@ -1,17 +1,15 @@ /* * Copyright 2013-2021 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 + * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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. + * 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.cloudfoundry.operations; @@ -46,6 +44,11 @@ import org.cloudfoundry.client.v2.userprovidedserviceinstances.UserProvidedServiceInstances; import org.cloudfoundry.client.v2.users.Users; import org.cloudfoundry.client.v3.applications.ApplicationsV3; +import org.cloudfoundry.client.v3.domains.DomainsV3; +import org.cloudfoundry.client.v3.jobs.JobsV3; +import org.cloudfoundry.client.v3.organizations.OrganizationsV3; +import org.cloudfoundry.client.v3.routes.RoutesV3; +import org.cloudfoundry.client.v3.spaces.SpacesV3; import org.cloudfoundry.client.v3.tasks.Tasks; import org.cloudfoundry.doppler.DopplerClient; import org.cloudfoundry.routing.RoutingClient; @@ -92,6 +95,7 @@ public abstract class AbstractOperationsTest { mock(CloudFoundryClient.class, RETURNS_SMART_NULLS); protected final Domains domains = mock(Domains.class, RETURNS_SMART_NULLS); + protected final DomainsV3 domainsV3 = mock(DomainsV3.class, RETURNS_SMART_NULLS); protected final DopplerClient dopplerClient = mock(DopplerClient.class, RETURNS_SMART_NULLS); @@ -100,11 +104,14 @@ public abstract class AbstractOperationsTest { protected final FeatureFlags featureFlags = mock(FeatureFlags.class, RETURNS_SMART_NULLS); protected final Jobs jobs = mock(Jobs.class, RETURNS_SMART_NULLS); + protected final JobsV3 jobsV3 = mock(JobsV3.class, RETURNS_SMART_NULLS); protected final OrganizationQuotaDefinitions organizationQuotaDefinitions = mock(OrganizationQuotaDefinitions.class, RETURNS_SMART_NULLS); protected final Organizations organizations = mock(Organizations.class, RETURNS_SMART_NULLS); + protected final OrganizationsV3 organizationsV3 = + mock(OrganizationsV3.class, RETURNS_SMART_NULLS); protected final PrivateDomains privateDomains = mock(PrivateDomains.class, RETURNS_SMART_NULLS); @@ -113,6 +120,7 @@ public abstract class AbstractOperationsTest { protected final RouterGroups routerGroups = mock(RouterGroups.class, RETURNS_SMART_NULLS); protected final Routes routes = mock(Routes.class, RETURNS_SMART_NULLS); + protected final RoutesV3 routesV3 = mock(RoutesV3.class, RETURNS_SMART_NULLS); protected final RoutingClient routingClient = mock(RoutingClient.class, RETURNS_SMART_NULLS); @@ -139,6 +147,7 @@ public abstract class AbstractOperationsTest { mock(SpaceQuotaDefinitions.class, RETURNS_SMART_NULLS); protected final Spaces spaces = mock(Spaces.class, RETURNS_SMART_NULLS); + protected final SpacesV3 spacesV3 = mock(SpacesV3.class, RETURNS_SMART_NULLS); protected final Stacks stacks = mock(Stacks.class, RETURNS_SMART_NULLS); @@ -162,15 +171,19 @@ public final void mockClient() { when(this.cloudFoundryClient.applicationsV3()).thenReturn(this.applicationsV3); when(this.cloudFoundryClient.buildpacks()).thenReturn(this.buildpacks); when(this.cloudFoundryClient.domains()).thenReturn(this.domains); + when(this.cloudFoundryClient.domainsV3()).thenReturn(this.domainsV3); when(this.cloudFoundryClient.events()).thenReturn(this.events); when(this.cloudFoundryClient.featureFlags()).thenReturn(this.featureFlags); when(this.cloudFoundryClient.jobs()).thenReturn(this.jobs); + when(this.cloudFoundryClient.jobsV3()).thenReturn(this.jobsV3); when(this.cloudFoundryClient.organizations()).thenReturn(this.organizations); + when(this.cloudFoundryClient.organizationsV3()).thenReturn(this.organizationsV3); when(this.cloudFoundryClient.organizationQuotaDefinitions()) .thenReturn(this.organizationQuotaDefinitions); when(this.cloudFoundryClient.privateDomains()).thenReturn(this.privateDomains); when(this.cloudFoundryClient.resourceMatch()).thenReturn(this.resourceMatch); when(this.cloudFoundryClient.routes()).thenReturn(this.routes); + when(this.cloudFoundryClient.routesV3()).thenReturn(this.routesV3); when(this.cloudFoundryClient.serviceBindingsV2()).thenReturn(this.serviceBindingsV2); when(this.cloudFoundryClient.serviceBrokers()).thenReturn(this.serviceBrokers); when(this.cloudFoundryClient.serviceInstances()).thenReturn(this.serviceInstances); @@ -183,6 +196,7 @@ public final void mockClient() { when(this.cloudFoundryClient.spaceQuotaDefinitions()) .thenReturn(this.spaceQuotaDefinitions); when(this.cloudFoundryClient.spaces()).thenReturn(this.spaces); + when(this.cloudFoundryClient.spacesV3()).thenReturn(this.spacesV3); when(this.cloudFoundryClient.stacks()).thenReturn(this.stacks); when(this.cloudFoundryClient.tasks()).thenReturn(this.tasks); when(this.cloudFoundryClient.userProvidedServiceInstances()) diff --git a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/routes/DefaultRoutesTest.java b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/routes/DefaultRoutesTest.java index 50fbdf5260..f4cabd9c5e 100644 --- a/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/routes/DefaultRoutesTest.java +++ b/cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/routes/DefaultRoutesTest.java @@ -1,17 +1,15 @@ /* * Copyright 2013-2021 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 + * 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 * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://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. + * 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.cloudfoundry.operations.routes; @@ -21,35 +19,24 @@ import static org.mockito.Mockito.when; import java.time.Duration; -import java.util.Arrays; import java.util.Collections; -import java.util.LinkedList; import java.util.Optional; -import java.util.Queue; -import java.util.function.Supplier; import org.cloudfoundry.client.CloudFoundryClient; -import org.cloudfoundry.client.v2.ClientV2Exception; import org.cloudfoundry.client.v2.Metadata; import org.cloudfoundry.client.v2.applications.ApplicationResource; import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteRequest; import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteResponse; import org.cloudfoundry.client.v2.applications.RemoveApplicationRouteRequest; -import org.cloudfoundry.client.v2.jobs.ErrorDetails; -import org.cloudfoundry.client.v2.jobs.GetJobRequest; -import org.cloudfoundry.client.v2.jobs.GetJobResponse; -import org.cloudfoundry.client.v2.jobs.JobEntity; import org.cloudfoundry.client.v2.organizations.ListOrganizationPrivateDomainsRequest; import org.cloudfoundry.client.v2.organizations.ListOrganizationPrivateDomainsResponse; import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesRequest; import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesResponse; import org.cloudfoundry.client.v2.privatedomains.PrivateDomainResource; import org.cloudfoundry.client.v2.routes.CreateRouteResponse; -import org.cloudfoundry.client.v2.routes.DeleteRouteResponse; import org.cloudfoundry.client.v2.routes.ListRouteApplicationsRequest; import org.cloudfoundry.client.v2.routes.ListRouteApplicationsResponse; import org.cloudfoundry.client.v2.routes.ListRoutesResponse; import org.cloudfoundry.client.v2.routes.RouteEntity; -import org.cloudfoundry.client.v2.routes.RouteExistsRequest; import org.cloudfoundry.client.v2.routes.RouteResource; import org.cloudfoundry.client.v2.serviceinstances.UnionServiceInstanceEntity; import org.cloudfoundry.client.v2.serviceinstances.UnionServiceInstanceResource; @@ -64,11 +51,24 @@ import org.cloudfoundry.client.v2.spaces.ListSpaceServiceInstancesResponse; import org.cloudfoundry.client.v2.spaces.SpaceEntity; import org.cloudfoundry.client.v2.spaces.SpaceResource; +import org.cloudfoundry.client.v3.Pagination; +import org.cloudfoundry.client.v3.Relationship; +import org.cloudfoundry.client.v3.ToManyRelationship; +import org.cloudfoundry.client.v3.ToOneRelationship; +import org.cloudfoundry.client.v3.domains.CheckReservedRoutesRequest; +import org.cloudfoundry.client.v3.domains.CheckReservedRoutesResponse; +import org.cloudfoundry.client.v3.domains.DomainRelationships; +import org.cloudfoundry.client.v3.domains.DomainResource; +import org.cloudfoundry.client.v3.jobs.GetJobRequest; +import org.cloudfoundry.client.v3.jobs.GetJobResponse; +import org.cloudfoundry.client.v3.jobs.JobState; +import org.cloudfoundry.client.v3.organizations.ListOrganizationDomainsRequest; +import org.cloudfoundry.client.v3.organizations.ListOrganizationDomainsResponse; +import org.cloudfoundry.client.v3.spaces.DeleteUnmappedRoutesRequest; import org.cloudfoundry.operations.AbstractOperationsTest; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import reactor.test.scheduler.VirtualTimeScheduler; final class DefaultRoutesTest extends AbstractOperationsTest { @@ -77,57 +77,24 @@ final class DefaultRoutesTest extends AbstractOperationsTest { Mono.just(this.cloudFoundryClient), Mono.just(TEST_ORGANIZATION_ID), Mono.just(TEST_SPACE_ID)); + private static final String TEST_DOMAIN_ID = "3a5d3d89-3f89-4f05-8188-8a2b298c79d5"; + private static final String TEST_DOMAIN_NAME = "domain-name"; + private static final String TEST_PATH = "test-path"; + private static final String TEST_HOST = "192.168.0,.1"; - @Test - void checkRouteInvalidDomain() { - requestPrivateDomainsEmpty(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestSharedDomainsEmpty(this.cloudFoundryClient, "test-domain"); - - this.routes - .check(CheckRouteRequest.builder().domain("test-domain").host("test-host").build()) - .as(StepVerifier::create) - .expectNext(false) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } + private static final String TEST_JOB_ID = "test-job-id"; @Test - void checkRouteInvalidHost() { - requestPrivateDomains(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestRouteExistsFalse( - this.cloudFoundryClient, - "test-private-domain-metadata-id", - "test-host", - "test-path"); - - this.routes - .check( - CheckRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") - .build()) - .as(StepVerifier::create) - .expectNext(false) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } - - @Test - void checkRoutePrivateDomain() { - requestPrivateDomains(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestRouteExistsTrue( - this.cloudFoundryClient, - "test-private-domain-metadata-id", - "test-host", - "test-path"); + void checkRoute() { + mockListDomains(this.cloudFoundryClient); + mockCheckReservedRoutes(this.cloudFoundryClient); this.routes .check( CheckRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") + .host(TEST_HOST) + .path(TEST_PATH) + .domain(TEST_DOMAIN_NAME) .build()) .as(StepVerifier::create) .expectNext(true) @@ -135,27 +102,63 @@ void checkRoutePrivateDomain() { .verify(Duration.ofSeconds(5)); } - @Test - void checkRouteSharedDomain() { - requestPrivateDomainsEmpty(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestSharedDomains(this.cloudFoundryClient, "test-domain"); - requestRouteExistsTrue( - this.cloudFoundryClient, - "test-shared-domain-metadata-id", - "test-host", - "test-path"); + private static void mockCheckReservedRoutes(CloudFoundryClient cloudFoundryClient) { + when(cloudFoundryClient + .domainsV3() + .checkReservedRoutes( + CheckReservedRoutesRequest.builder() + .domainId(TEST_DOMAIN_ID) + .host(TEST_HOST) + .path(TEST_PATH) + .build())) + .thenReturn( + Mono.just( + CheckReservedRoutesResponse.builder().matchingRoute(true).build())); + } - this.routes - .check( - CheckRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") - .build()) - .as(StepVerifier::create) - .expectNext(true) - .expectComplete() - .verify(Duration.ofSeconds(5)); + private static void mockListDomains(CloudFoundryClient cloudFoundryClient) { + when(cloudFoundryClient + .organizationsV3() + .listDomains( + ListOrganizationDomainsRequest.builder() + .name(TEST_DOMAIN_NAME) + .page(1) + .organizationId(TEST_ORGANIZATION_ID) + .build())) + .thenReturn( + Mono.just( + ListOrganizationDomainsResponse.builder() + .pagination( + Pagination.builder() + .totalResults(1) + .totalPages(1) + .build()) + .resource( + DomainResource.builder() + .id(TEST_DOMAIN_ID) + .createdAt("2019-03-08T01:06:19Z") + .updatedAt("2019-03-08T01:06:19Z") + .name(TEST_DOMAIN_NAME) + .isInternal(false) + .relationships( + DomainRelationships.builder() + .organization( + ToOneRelationship + .builder() + .data( + Relationship + .builder() + .id( + TEST_ORGANIZATION_ID) + .build()) + .build()) + .sharedOrganizations( + ToManyRelationship + .builder() + .build()) + .build()) + .build()) + .build())); } @Test @@ -336,224 +339,71 @@ void createRouteRandomPort() { } @Test - void deleteAssignedPort() { - requestPrivateDomainsEmpty(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestSharedDomains(this.cloudFoundryClient, "test-domain"); - requestRoutes(this.cloudFoundryClient, "test-shared-domain-metadata-id", null, null, 9999); - requestDeleteRoute(this.cloudFoundryClient, "test-route-id"); - requestJobSuccess(this.cloudFoundryClient, "test-job-entity-id"); - - StepVerifier.withVirtualTime( - () -> - this.routes.delete( - DeleteRouteRequest.builder() - .domain("test-domain") - .port(9999) - .build())) - .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(3))) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteFailure() { - requestPrivateDomains(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestRoutes( - this.cloudFoundryClient, - "test-private-domain-metadata-id", - "test-host", - "test-path", - null); - requestDeleteRoute(this.cloudFoundryClient, "test-route-id"); - requestJobFailure(this.cloudFoundryClient, "test-job-entity-id"); - - StepVerifier.withVirtualTime( - () -> - this.routes.delete( - DeleteRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") - .build())) - .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(3))) - .consumeErrorWith( - t -> - assertThat(t) - .isInstanceOf(ClientV2Exception.class) - .hasMessage( - "test-error-details-errorCode(1):" - + " test-error-details-description")) - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteInvalidDomain() { - requestPrivateDomainsEmpty(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestSharedDomainsEmpty(this.cloudFoundryClient, "test-domain"); + void deleteOrphanedRoutes() { + mockDeleteOrphanedRoutes(this.cloudFoundryClient); this.routes - .delete( - DeleteRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") - .build()) + .deleteOrphanedRoutes(DeleteOrphanedRoutesRequest.builder().build()) .as(StepVerifier::create) - .consumeErrorWith( - t -> - assertThat(t) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Domain test-domain does not exist")) + .expectComplete() .verify(Duration.ofSeconds(5)); } + private static void mockDeleteOrphanedRoutes(CloudFoundryClient cloudFoundryClient) { + when(cloudFoundryClient + .spacesV3() + .deleteUnmappedRoutes( + DeleteUnmappedRoutesRequest.builder() + .spaceId(TEST_SPACE_ID) + .build())) + .thenReturn(Mono.just(TEST_JOB_ID)); + when(cloudFoundryClient.jobsV3().get(GetJobRequest.builder().jobId(TEST_JOB_ID).build())) + .thenReturn( + Mono.just(fill(GetJobResponse.builder()).state(JobState.COMPLETE).build())); + } + @Test - void deleteInvalidRoute() { + void deleteRoute() { requestPrivateDomains(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestRoutesEmpty( + requestRoutes( this.cloudFoundryClient, "test-private-domain-metadata-id", "test-host", "test-path", null); + mockDeleteRequest(this.cloudFoundryClient, "test-route-id"); this.routes .delete( DeleteRouteRequest.builder() - .domain("test-domain") .host("test-host") .path("test-path") + .domain("test-domain") .build()) .as(StepVerifier::create) - .consumeErrorWith( - t -> - assertThat(t) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Route for test-domain does not exist")) - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteOrphanedRoutesAssociatedApplication() { - requestSpaceRoutes(this.cloudFoundryClient, TEST_SPACE_ID); - requestApplications(this.cloudFoundryClient, "test-route-id"); - - this.routes - .deleteOrphanedRoutes(DeleteOrphanedRoutesRequest.builder().build()) - .as(StepVerifier::create) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteOrphanedRoutesAssociatedService() { - requestSpaceRoutesService(this.cloudFoundryClient, TEST_SPACE_ID); - - this.routes - .deleteOrphanedRoutes(DeleteOrphanedRoutesRequest.builder().build()) - .as(StepVerifier::create) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteOrphanedRoutesNoAssociations() { - requestSpaceRoutes(this.cloudFoundryClient, TEST_SPACE_ID); - requestApplicationsEmpty(this.cloudFoundryClient, "test-route-id"); - requestDeleteRoute(this.cloudFoundryClient, "test-route-id"); - requestJobSuccess(this.cloudFoundryClient, "test-job-entity-id"); - - StepVerifier.withVirtualTime( - () -> - this.routes.deleteOrphanedRoutes( - DeleteOrphanedRoutesRequest.builder().build())) - .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(3))) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteOrphanedRoutesNoAssociationsFailure() { - requestSpaceRoutes(this.cloudFoundryClient, TEST_SPACE_ID); - requestApplicationsEmpty(this.cloudFoundryClient, "test-route-id"); - requestDeleteRoute(this.cloudFoundryClient, "test-route-id"); - requestJobFailure(this.cloudFoundryClient, "test-job-entity-id"); - - StepVerifier.withVirtualTime( - () -> - this.routes.deleteOrphanedRoutes( - DeleteOrphanedRoutesRequest.builder().build())) - .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(3))) - .consumeErrorWith( - t -> - assertThat(t) - .isInstanceOf(ClientV2Exception.class) - .hasMessage( - "test-error-details-errorCode(1):" - + " test-error-details-description")) - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteOrphanedRoutesNoRoutes() { - requestSpaceRoutesEmpty(this.cloudFoundryClient, TEST_SPACE_ID); - - this.routes - .deleteOrphanedRoutes(DeleteOrphanedRoutesRequest.builder().build()) - .as(StepVerifier::create) .expectComplete() .verify(Duration.ofSeconds(5)); } - @Test - void deletePrivateDomain() { - requestPrivateDomains(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestRoutes( - this.cloudFoundryClient, - "test-private-domain-metadata-id", - "test-host", - "test-path", - null); - requestDeleteRoute(this.cloudFoundryClient, "test-route-id"); - requestJobSuccess(this.cloudFoundryClient, "test-job-entity-id"); - - StepVerifier.withVirtualTime( - () -> - this.routes.delete( - DeleteRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") - .build())) - .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(3))) - .expectComplete() - .verify(Duration.ofSeconds(5)); - } - - @Test - void deleteSharedDomain() { - requestPrivateDomainsEmpty(this.cloudFoundryClient, TEST_ORGANIZATION_ID, "test-domain"); - requestSharedDomains(this.cloudFoundryClient, "test-domain"); - requestRoutes( - this.cloudFoundryClient, - "test-shared-domain-metadata-id", - "test-host", - "test-path", - null); - requestDeleteRoute(this.cloudFoundryClient, "test-route-id"); - requestJobSuccess(this.cloudFoundryClient, "test-job-entity-id"); - - StepVerifier.withVirtualTime( - () -> - this.routes.delete( - DeleteRouteRequest.builder() - .domain("test-domain") - .host("test-host") - .path("test-path") - .build())) - .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofSeconds(3))) - .expectComplete() - .verify(Duration.ofSeconds(5)); + private static void mockDeleteRequest(CloudFoundryClient cloudFoundryClient, String routeId) { + when(cloudFoundryClient + .routesV3() + .delete( + org.cloudfoundry.client.v3.routes.DeleteRouteRequest.builder() + .routeId(routeId) + .build())) + .thenReturn(Mono.just("test-delete-job")); + when(cloudFoundryClient + .jobsV3() + .get( + org.cloudfoundry.client.v3.jobs.GetJobRequest.builder() + .jobId("test-delete-job") + .build())) + .thenReturn( + Mono.just( + fill(org.cloudfoundry.client.v3.jobs.GetJobResponse.builder()) + .state(org.cloudfoundry.client.v3.jobs.JobState.COMPLETE) + .build())); } @Test @@ -1126,93 +976,6 @@ private static void requestCreateRoute( .thenReturn(Mono.just(fill(CreateRouteResponse.builder(), "route-").build())); } - private static void requestDeleteRoute(CloudFoundryClient cloudFoundryClient, String routeId) { - when(cloudFoundryClient - .routes() - .delete( - org.cloudfoundry.client.v2.routes.DeleteRouteRequest.builder() - .async(true) - .routeId(routeId) - .build())) - .thenReturn( - Mono.just( - fill(DeleteRouteResponse.builder()) - .entity(fill(JobEntity.builder(), "job-entity-").build()) - .build())); - } - - private static void requestJobFailure(CloudFoundryClient cloudFoundryClient, String jobId) { - when(cloudFoundryClient.jobs().get(GetJobRequest.builder().jobId(jobId).build())) - .thenReturn( - Mono.defer( - new Supplier>() { - - private final Queue responses = - new LinkedList<>( - Arrays.asList( - fill(GetJobResponse.builder(), "job-") - .entity( - fill(JobEntity - .builder()) - .status( - "running") - .build()) - .build(), - fill(GetJobResponse.builder(), "job-") - .entity( - fill(JobEntity - .builder()) - .errorDetails( - fill( - ErrorDetails - .builder(), - "error-details-") - .build()) - .status( - "failed") - .build()) - .build())); - - @Override - public Mono get() { - return Mono.just(this.responses.poll()); - } - })); - } - - private static void requestJobSuccess(CloudFoundryClient cloudFoundryClient, String jobId) { - when(cloudFoundryClient.jobs().get(GetJobRequest.builder().jobId(jobId).build())) - .thenReturn( - Mono.defer( - new Supplier>() { - - private final Queue responses = - new LinkedList<>( - Arrays.asList( - fill(GetJobResponse.builder(), "job-") - .entity( - fill(JobEntity - .builder()) - .status( - "running") - .build()) - .build(), - fill(GetJobResponse.builder(), "job-") - .entity( - fill(JobEntity - .builder()) - .status( - "finished") - .build()) - .build())); - - @Override - public Mono get() { - return Mono.just(this.responses.poll()); - } - })); - } - private static void requestOrganizationsRoutes( CloudFoundryClient cloudFoundryClient, String organizationId) { when(cloudFoundryClient @@ -1327,32 +1090,6 @@ private static void requestRemoveRouteFromApplication( .thenReturn(Mono.empty()); } - private static void requestRouteExistsFalse( - CloudFoundryClient cloudFoundryClient, String domainId, String host, String path) { - when(cloudFoundryClient - .routes() - .exists( - RouteExistsRequest.builder() - .domainId(domainId) - .host(host) - .path(path) - .build())) - .thenReturn(Mono.just(false)); - } - - private static void requestRouteExistsTrue( - CloudFoundryClient cloudFoundryClient, String domainId, String host, String path) { - when(cloudFoundryClient - .routes() - .exists( - RouteExistsRequest.builder() - .domainId(domainId) - .host(host) - .path(path) - .build())) - .thenReturn(Mono.just(true)); - } - private static void requestRoutes( CloudFoundryClient cloudFoundryClient, String domainId,