/* * Copyright 2013-2017 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 * * 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. */ package org.cloudfoundry.operations; import org.cloudfoundry.AbstractIntegrationTest; import org.cloudfoundry.operations.applications.ApplicationHealthCheck; import org.cloudfoundry.operations.applications.PushApplicationRequest; import org.cloudfoundry.operations.domains.CreateDomainRequest; import org.cloudfoundry.operations.domains.CreateSharedDomainRequest; import org.cloudfoundry.operations.routes.CheckRouteRequest; import org.cloudfoundry.operations.routes.CreateRouteRequest; import org.cloudfoundry.operations.routes.DeleteOrphanedRoutesRequest; import org.cloudfoundry.operations.routes.DeleteRouteRequest; import org.cloudfoundry.operations.routes.ListRoutesRequest; import org.cloudfoundry.operations.routes.MapRouteRequest; import org.cloudfoundry.operations.routes.Route; import org.cloudfoundry.operations.routes.UnmapRouteRequest; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import java.io.IOException; import java.nio.file.Path; import java.time.Duration; import java.util.Collections; import java.util.Optional; import java.util.concurrent.TimeoutException; import java.util.function.Predicate; import static org.assertj.core.api.Assertions.assertThat; import static org.cloudfoundry.operations.routes.Level.ORGANIZATION; import static org.cloudfoundry.operations.routes.Level.SPACE; import static org.cloudfoundry.util.tuple.TupleUtils.function; public final class RoutesTest extends AbstractIntegrationTest { private static final String DEFAULT_ROUTER_GROUP = "default-tcp"; @Autowired private CloudFoundryOperations cloudFoundryOperations; @Autowired private String organizationName; @Autowired private String spaceName; @Test public void checkFalse() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String host = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); this.cloudFoundryOperations.routes() .check(CheckRouteRequest.builder() .domain(domainName) .host(host) .path(path) .build()) .as(StepVerifier::create) .expectNext(false) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void checkTruePrivateDomainNoHost() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = null; String path = this.nameFactory.getPath(); createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path) .then(this.cloudFoundryOperations.routes() .check(CheckRouteRequest.builder() .domain(domainName) .path(path) .build())) .as(StepVerifier::create) .expectNext(true) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void checkTrueSharedDomain() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); createSharedDomainAndRoute(this.cloudFoundryOperations, this.spaceName, domainName, hostName, path) .then(this.cloudFoundryOperations.routes() .check(CheckRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .build())) .as(StepVerifier::create) .expectNext(true) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void create() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path) .then(this.cloudFoundryOperations.routes() .check(CheckRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .build())) .as(StepVerifier::create) .expectNext(true) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void createRouteTcpAssignedPort() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); Integer port = 61002; requestCreateSharedDomain(this.cloudFoundryOperations, domainName, DEFAULT_ROUTER_GROUP) .then(requestCreateRoute(this.cloudFoundryOperations, this.spaceName, domainName, port)) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(response -> domainName.equals(response.getDomain())) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void createRouteTcpRandomPort() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); requestCreateSharedDomain(this.cloudFoundryOperations, domainName, DEFAULT_ROUTER_GROUP) .then(requestCreateRoute(this.cloudFoundryOperations, this.spaceName, domainName, true)) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(response -> domainName.equals(response.getDomain())) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void createRouteWithNonExistentDomain() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); this.cloudFoundryOperations.routes() .create(CreateRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .space(this.spaceName) .build()) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t).isInstanceOf(IllegalArgumentException.class).hasMessage("Domain %s does not exist", domainName)) .verify(Duration.ofMinutes(5)); } @Test public void delete() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path) .then(this.cloudFoundryOperations.routes() .delete(DeleteRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .build())) .then(this.cloudFoundryOperations.routes() .check(CheckRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .build())) .as(StepVerifier::create) .expectNext(false) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void deleteInvalidDomain() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); this.cloudFoundryOperations.routes() .delete(DeleteRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .build()) .as(StepVerifier::create) .consumeErrorWith(t -> assertThat(t).isInstanceOf(IllegalArgumentException.class).hasMessage("Domain %s does not exist", domainName)) .verify(Duration.ofMinutes(5)); } @Test public void deleteOrphanedRoutes() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path) .then(this.cloudFoundryOperations.routes() .deleteOrphanedRoutes(DeleteOrphanedRoutesRequest.builder() .build())) .then(this.cloudFoundryOperations.routes() .check(CheckRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .build())) .as(StepVerifier::create) .expectNext(false) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void deleteTcpRoute() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); requestCreateSharedDomain(this.cloudFoundryOperations, domainName, DEFAULT_ROUTER_GROUP) .then(requestCreateRoute(this.cloudFoundryOperations, this.spaceName, domainName, true)) .then(port -> this.cloudFoundryOperations.routes() .delete(DeleteRouteRequest.builder() .domain(domainName) .port(port) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(response -> domainName.equals(response.getDomain())) .as(StepVerifier::create) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void listWithOrganizationLevel() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path) .thenMany(this.cloudFoundryOperations.routes() .list(ListRoutesRequest.builder() .level(ORGANIZATION) .build())) .filter(filterRoutes(domainName, hostName, path, null)) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void listWithSpaceLevel() throws TimeoutException, InterruptedException { String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path) .thenMany(this.cloudFoundryOperations.routes() .list(ListRoutesRequest.builder() .level(SPACE) .build())) .filter(filterRoutes(domainName, hostName, path, null)) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void map() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); Mono .when( createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(this.cloudFoundryOperations.routes() .map(MapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .host(hostName) .path(path) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(filterRoutes(domainName, hostName, path, applicationName)) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void mapNoHost() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); String hostName = null; String path = this.nameFactory.getPath(); Mono .when( createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(this.cloudFoundryOperations.routes() .map(MapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .host(hostName) .path(path) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(filterRoutes(domainName, hostName, path, applicationName)) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void mapNoPath() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = null; Mono .when( createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(this.cloudFoundryOperations.routes() .map(MapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .host(hostName) .path(path) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(filterRoutes(domainName, hostName, path, applicationName)) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void mapTcpRoute() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); Mono .when( createSharedDomainAndTcpRoute(this.cloudFoundryOperations, domainName, this.spaceName), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(this.cloudFoundryOperations.routes() .map(MapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(response -> domainName.equals(response.getDomain())) .single() .map(route -> route.getApplications().size()) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void unmap() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = this.nameFactory.getPath(); Mono .when( createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(requestMapRoute(this.cloudFoundryOperations, applicationName, domainName, hostName, path)) .then(this.cloudFoundryOperations.routes() .unmap(UnmapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .host(hostName) .path(path) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(filterRoutes(domainName, hostName, path, applicationName)) .as(StepVerifier::create) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void unmapNoPath() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); String hostName = this.nameFactory.getHostName(); String path = null; Mono .when( createDomainAndRoute(this.cloudFoundryOperations, this.organizationName, this.spaceName, domainName, hostName, path), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(requestMapRoute(this.cloudFoundryOperations, applicationName, domainName, hostName, path)) .then(this.cloudFoundryOperations.routes() .unmap(UnmapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .host(hostName) .path(path) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(filterRoutes(domainName, hostName, path, applicationName)) .as(StepVerifier::create) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void unmapTcpRoute() throws TimeoutException, InterruptedException, IOException { String applicationName = this.nameFactory.getApplicationName(); String domainName = this.nameFactory.getDomainName(); Mono .when( createSharedDomainAndTcpRoute(this.cloudFoundryOperations, domainName, this.spaceName), requestCreateApplication(this.cloudFoundryOperations, new ClassPathResource("test-application.zip").getFile().toPath(), applicationName, true) ) .then(function((port, ignore) -> requestMapRoute(this.cloudFoundryOperations, applicationName, domainName, port))) .then(port -> this.cloudFoundryOperations.routes() .unmap(UnmapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .port(port) .build())) .thenMany(requestListRoutes(this.cloudFoundryOperations)) .filter(response -> domainName.equals(response.getDomain())) .map(route -> route.getApplications().size()) .as(StepVerifier::create) .expectNext(0) .expectComplete() .verify(Duration.ofMinutes(5)); } private static Mono<Integer> createDomainAndRoute(CloudFoundryOperations cloudFoundryOperations, String organizationName, String spaceName, String domainName, String hostName, String path) { return requestCreateDomain(cloudFoundryOperations, organizationName, domainName) .then(requestCreateRoute(cloudFoundryOperations, spaceName, domainName, hostName, path)); } private static Mono<Integer> createSharedDomainAndRoute(CloudFoundryOperations cloudFoundryOperations, String spaceName, String domainName, String hostName, String path) { return requestCreateSharedDomain(cloudFoundryOperations, domainName, null) .then(requestCreateRoute(cloudFoundryOperations, spaceName, domainName, hostName, path)); } private static Mono<Integer> createSharedDomainAndTcpRoute(CloudFoundryOperations cloudFoundryOperations, String domainName, String spaceName) { return requestCreateSharedDomain(cloudFoundryOperations, domainName, DEFAULT_ROUTER_GROUP) .then(requestCreateRoute(cloudFoundryOperations, spaceName, domainName, true)); } private static Predicate<Route> filterRoutes(String domainName, String host, String path, String applicationName) { return route -> Optional.ofNullable(domainName).map(route.getDomain()::equals).orElse(true) && Optional.ofNullable(host).map(route.getHost()::equals).orElse(true) && Optional.ofNullable(applicationName).map(Collections::singletonList).map(route.getApplications()::equals).orElse(true) && Optional.ofNullable(path).map(route.getPath()::equals).orElse(true); } private static Mono<Void> requestCreateApplication(CloudFoundryOperations cloudFoundryOperations, Path application, String name, Boolean noStart) { return cloudFoundryOperations.applications() .push(PushApplicationRequest.builder() .path(application) .healthCheckType(ApplicationHealthCheck.PORT) .buildpack("staticfile_buildpack") .diskQuota(512) .memory(64) .name(name) .noStart(noStart) .build()); } private static Mono<Void> requestCreateDomain(CloudFoundryOperations cloudFoundryOperations, String organizationName, String domainName) { return cloudFoundryOperations.domains() .create(CreateDomainRequest.builder() .domain(domainName) .organization(organizationName) .build()); } private static Mono<Integer> requestCreateRoute(CloudFoundryOperations cloudFoundryOperations, String spaceName, String domainName, String hostName, String path) { return cloudFoundryOperations.routes() .create(CreateRouteRequest.builder() .domain(domainName) .host(hostName) .path(path) .space(spaceName) .build()); } private static Mono<Integer> requestCreateRoute(CloudFoundryOperations cloudFoundryOperations, String spaceName, String domainName, Integer port) { return cloudFoundryOperations.routes() .create(CreateRouteRequest.builder() .domain(domainName) .port(port) .space(spaceName) .build()); } private static Mono<Integer> requestCreateRoute(CloudFoundryOperations cloudFoundryOperations, String spaceName, String domainName, Boolean randomPort) { return cloudFoundryOperations.routes() .create(CreateRouteRequest.builder() .domain(domainName) .randomPort(randomPort) .space(spaceName) .build()); } private static Mono<Void> requestCreateSharedDomain(CloudFoundryOperations cloudFoundryOperations, String domainName, String routerGroupId) { return cloudFoundryOperations.domains() .createShared(CreateSharedDomainRequest.builder() .domain(domainName) .routerGroup(routerGroupId) .build()); } private static Flux<Route> requestListRoutes(CloudFoundryOperations cloudFoundryOperations) { return cloudFoundryOperations.routes() .list(ListRoutesRequest.builder() .level(SPACE) .build()); } private static Mono<Integer> requestMapRoute(CloudFoundryOperations cloudFoundryOperations, String applicationName, String domainName, String hostName, String path) { return cloudFoundryOperations.routes() .map(MapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .host(hostName) .path(path) .build()); } private static Mono<Integer> requestMapRoute(CloudFoundryOperations cloudFoundryOperations, String applicationName, String domainName, Integer port) { return cloudFoundryOperations.routes() .map(MapRouteRequest.builder() .applicationName(applicationName) .domain(domainName) .port(port) .build()); } }