/* * 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.client.v2; import org.cloudfoundry.AbstractIntegrationTest; import org.cloudfoundry.client.CloudFoundryClient; import org.cloudfoundry.client.v2.applications.ApplicationInstanceInfo; import org.cloudfoundry.client.v2.applications.ApplicationInstancesRequest; import org.cloudfoundry.client.v2.applications.ApplicationInstancesResponse; import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteRequest; import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteResponse; import org.cloudfoundry.client.v2.applications.CreateApplicationRequest; import org.cloudfoundry.client.v2.applications.CreateApplicationResponse; import org.cloudfoundry.client.v2.applications.DeleteApplicationRequest; import org.cloudfoundry.client.v2.applications.GetApplicationRequest; import org.cloudfoundry.client.v2.applications.GetApplicationResponse; import org.cloudfoundry.client.v2.applications.UpdateApplicationRequest; import org.cloudfoundry.client.v2.applications.UpdateApplicationResponse; import org.cloudfoundry.client.v2.applications.UploadApplicationRequest; import org.cloudfoundry.client.v2.applications.UploadApplicationResponse; import org.cloudfoundry.client.v2.routes.CreateRouteRequest; import org.cloudfoundry.client.v2.routes.CreateRouteResponse; import org.cloudfoundry.client.v2.servicebrokers.CreateServiceBrokerRequest; import org.cloudfoundry.client.v2.servicebrokers.DeleteServiceBrokerRequest; import org.cloudfoundry.client.v2.servicebrokers.GetServiceBrokerRequest; import org.cloudfoundry.client.v2.servicebrokers.ListServiceBrokersRequest; import org.cloudfoundry.client.v2.servicebrokers.UpdateServiceBrokerRequest; import org.cloudfoundry.client.v2.shareddomains.ListSharedDomainsRequest; import org.cloudfoundry.client.v2.shareddomains.SharedDomainResource; import org.cloudfoundry.client.v2.spaces.CreateSpaceRequest; import org.cloudfoundry.client.v2.spaces.CreateSpaceResponse; import org.cloudfoundry.util.JobUtils; import org.cloudfoundry.util.PaginationUtils; import org.cloudfoundry.util.ResourceUtils; 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 static org.assertj.core.api.Assertions.assertThat; import static org.cloudfoundry.util.DelayUtils.exponentialBackOff; import static org.cloudfoundry.util.tuple.TupleUtils.function; public final class ServiceBrokersTest extends AbstractIntegrationTest { private final Path application = new ClassPathResource("test-service-broker.jar").getFile().toPath(); @Autowired private CloudFoundryClient cloudFoundryClient; @Autowired private Mono<String> organizationId; @Autowired private Mono<String> serviceBrokerId; @Autowired private String serviceBrokerName; public ServiceBrokersTest() throws IOException { } @Test public void create() { String serviceBrokerName = this.nameFactory.getServiceBrokerName(); String serviceName = this.nameFactory.getServiceName(); String planName = this.nameFactory.getPlanName(); ApplicationMetadata applicationMetadata = this.organizationId .then(organizationId -> pushServiceBroker(this.cloudFoundryClient, organizationId, serviceName, planName)) .block(Duration.ofMinutes(5)); this.cloudFoundryClient.serviceBrokers() .create(CreateServiceBrokerRequest.builder() .authenticationPassword("test-authentication-password") .authenticationUsername("test-authentication-username") .brokerUrl(applicationMetadata.uri) .name(serviceBrokerName) .spaceId(applicationMetadata.spaceId) .build()) .flatMapMany(response -> PaginationUtils .requestClientV2Resources(page -> this.cloudFoundryClient.serviceBrokers() .list(ListServiceBrokersRequest.builder() .name(serviceBrokerName) .page(page) .build()))) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); deleteServiceBroker(this.cloudFoundryClient, applicationMetadata.applicationId) .block(Duration.ofMinutes(5)); } @Test public void delete() { String serviceBrokerName = this.nameFactory.getServiceBrokerName(); String serviceName = this.nameFactory.getServiceName(); String planName = this.nameFactory.getPlanName(); ServiceBrokerMetadata serviceBrokerMetadata = this.organizationId .then(organizationId -> createServiceBroker(this.cloudFoundryClient, organizationId, serviceBrokerName, serviceName, planName)) .block(Duration.ofMinutes(5)); this.cloudFoundryClient.serviceBrokers() .delete(DeleteServiceBrokerRequest.builder() .serviceBrokerId(serviceBrokerMetadata.serviceBrokerId) .build()) .flatMapMany(response -> PaginationUtils .requestClientV2Resources(page -> this.cloudFoundryClient.serviceBrokers() .list(ListServiceBrokersRequest.builder() .name(serviceBrokerName) .page(page) .build()))) .as(StepVerifier::create) .expectComplete() .verify(Duration.ofMinutes(5)); deleteServiceBroker(this.cloudFoundryClient, serviceBrokerMetadata.applicationMetadata.applicationId) .block(Duration.ofMinutes(5)); } @Test public void get() { this.serviceBrokerId .then(serviceBrokerId -> this.cloudFoundryClient.serviceBrokers() .get(GetServiceBrokerRequest.builder() .serviceBrokerId(serviceBrokerId) .build())) .as(StepVerifier::create) .assertNext(serviceBroker -> assertThat(ResourceUtils.getEntity(serviceBroker).getName()).isEqualTo(this.serviceBrokerName)) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void list() { PaginationUtils .requestClientV2Resources(page -> this.cloudFoundryClient.serviceBrokers() .list(ListServiceBrokersRequest.builder() .name(this.serviceBrokerName) .page(page) .build())) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); } @Test public void update() { String serviceBrokerName1 = this.nameFactory.getServiceBrokerName(); String serviceBrokerName2 = this.nameFactory.getServiceBrokerName(); String serviceName = this.nameFactory.getServiceName(); String planName = this.nameFactory.getPlanName(); ServiceBrokerMetadata serviceBrokerMetadata = this.organizationId .then(organizationId -> createServiceBroker(this.cloudFoundryClient, organizationId, serviceBrokerName1, serviceName, planName)) .block(Duration.ofMinutes(5)); this.cloudFoundryClient.serviceBrokers() .update(UpdateServiceBrokerRequest.builder() .serviceBrokerId(serviceBrokerMetadata.serviceBrokerId) .name(serviceBrokerName2) .build()) .flatMapMany(serviceBrokerId -> PaginationUtils .requestClientV2Resources(page -> this.cloudFoundryClient.serviceBrokers() .list(ListServiceBrokersRequest.builder() .name(serviceBrokerName2) .page(page) .build()))) .as(StepVerifier::create) .expectNextCount(1) .expectComplete() .verify(Duration.ofMinutes(5)); deleteServiceBroker(this.cloudFoundryClient, serviceBrokerMetadata.applicationMetadata.applicationId) .block(Duration.ofMinutes(5)); } private static Mono<Void> deleteServiceBroker(CloudFoundryClient cloudFoundryClient, String applicationId) { return cloudFoundryClient.applicationsV2() .delete(DeleteApplicationRequest.builder() .applicationId(applicationId) .build()); } private static Mono<ApplicationInstancesResponse> requestApplicationInstances(CloudFoundryClient cloudFoundryClient, String applicationId) { return cloudFoundryClient.applicationsV2() .instances(ApplicationInstancesRequest.builder() .applicationId(applicationId) .build()); } private static Mono<AssociateApplicationRouteResponse> requestAssociateApplicationRoute(CloudFoundryClient cloudFoundryClient, String applicationId, String routeId) { return cloudFoundryClient.applicationsV2() .associateRoute(AssociateApplicationRouteRequest.builder() .applicationId(applicationId) .routeId(routeId) .build()); } private static Mono<CreateApplicationResponse> requestCreateApplication(CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName) { return cloudFoundryClient.applicationsV2() .create(CreateApplicationRequest.builder() .buildpack("https://github.com/cloudfoundry/java-buildpack.git") .memory(768) .name(applicationName) .spaceId(spaceId) .build()); } private static Mono<CreateRouteResponse> requestCreateRoute(CloudFoundryClient cloudFoundryClient, String domainId, String spaceId, String hostName) { return cloudFoundryClient.routes() .create(CreateRouteRequest.builder() .domainId(domainId) .host(hostName) .spaceId(spaceId) .build()); } private static Mono<CreateSpaceResponse> requestCreateSpace(CloudFoundryClient cloudFoundryClient, String organizationId, String spaceName) { return cloudFoundryClient.spaces() .create(CreateSpaceRequest.builder() .name(spaceName) .organizationId(organizationId) .build()); } private static Mono<GetApplicationResponse> requestGetApplication(CloudFoundryClient cloudFoundryClient, String applicationId) { return cloudFoundryClient.applicationsV2() .get(GetApplicationRequest.builder() .applicationId(applicationId) .build()); } private static Flux<SharedDomainResource> requestListSharedDomains(CloudFoundryClient cloudFoundryClient) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.sharedDomains() .list(ListSharedDomainsRequest.builder() .page(page) .build())); } private static Mono<UpdateApplicationResponse> requestUpdateApplication(CloudFoundryClient cloudFoundryClient, String applicationId, String state, String serviceName, String planName) { return cloudFoundryClient.applicationsV2() .update(UpdateApplicationRequest.builder() .applicationId(applicationId) .environmentJson("SERVICE_NAME", serviceName) .environmentJson("PLAN_NAME", planName) .state(state) .build()); } private static Mono<UploadApplicationResponse> requestUploadApplication(CloudFoundryClient cloudFoundryClient, String applicationId, Path application) { return cloudFoundryClient.applicationsV2() .upload(UploadApplicationRequest.builder() .application(application) .applicationId(applicationId) .async(true) .build()); } private Mono<ServiceBrokerMetadata> createServiceBroker(CloudFoundryClient cloudFoundryClient, String organizationId, String serviceBrokerName, String serviceName, String planName) { return pushServiceBroker(cloudFoundryClient, organizationId, serviceName, planName) .then(applicationMetadata -> this.cloudFoundryClient.serviceBrokers() .create(CreateServiceBrokerRequest.builder() .authenticationPassword("test-authentication-password") .authenticationUsername("test-authentication-username") .brokerUrl(applicationMetadata.uri) .name(serviceBrokerName) .spaceId(applicationMetadata.spaceId) .build()) .map(response -> new ServiceBrokerMetadata(applicationMetadata, ResourceUtils.getId(response)))); } private Mono<ApplicationMetadata> pushServiceBroker(CloudFoundryClient cloudFoundryClient, String organizationId, String serviceName, String planName) { String applicationName = this.nameFactory.getApplicationName(); String hostName = this.nameFactory.getHostName(); String spaceName = this.nameFactory.getSpaceName(); return Mono .when( requestCreateSpace(cloudFoundryClient, organizationId, spaceName) .map(ResourceUtils::getId), requestListSharedDomains(cloudFoundryClient) .next() ) .then(function((spaceId, domain) -> Mono .when( requestCreateApplication(cloudFoundryClient, spaceId, applicationName) .map(ResourceUtils::getId), Mono.just(ResourceUtils.getId(domain)) .then(domainId -> requestCreateRoute(cloudFoundryClient, domainId, spaceId, hostName)) .map(ResourceUtils::getId) ) .then(function((applicationId, routeId) -> requestAssociateApplicationRoute(cloudFoundryClient, applicationId, routeId) .then(Mono.just(applicationId)))) .then(applicationId -> requestUploadApplication(cloudFoundryClient, applicationId, this.application) .then(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job)) .then(Mono.just(applicationId))) .then(applicationId -> requestUpdateApplication(cloudFoundryClient, applicationId, "STARTED", serviceName, planName) .then(Mono.just(applicationId))) .then(applicationId -> requestGetApplication(cloudFoundryClient, applicationId) .map(response -> ResourceUtils.getEntity(response).getPackageState()) .filter(state -> "STAGED".equals(state) || "FAILED".equals(state)) .repeatWhenEmpty(exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5))) .then(Mono.just(applicationId))) .then(applicationId -> requestApplicationInstances(cloudFoundryClient, applicationId) .flatMapMany(response -> Flux.fromIterable(response.getInstances().values())) .single() .map(ApplicationInstanceInfo::getState) .filter("RUNNING"::equals) .repeatWhenEmpty(exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5))) .map(status -> new ApplicationMetadata(applicationId, spaceId, String.format("https://%s.%s", hostName, ResourceUtils.getEntity(domain).getName())))) )); } private static final class ApplicationMetadata { private final String applicationId; private final String spaceId; private final String uri; private ApplicationMetadata(String applicationId, String spaceId, String uri) { this.applicationId = applicationId; this.spaceId = spaceId; this.uri = uri; } } private static final class ServiceBrokerMetadata { private final ApplicationMetadata applicationMetadata; private final String serviceBrokerId; private ServiceBrokerMetadata(ApplicationMetadata applicationMetadata, String serviceBrokerId) { this.applicationMetadata = applicationMetadata; this.serviceBrokerId = serviceBrokerId; } } }