/*
* 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.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.cloudfoundry.AbstractIntegrationTest;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.applications.AbstractApplicationResource;
import org.cloudfoundry.client.v2.applications.ApplicationEnvironmentRequest;
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.ApplicationStatisticsRequest;
import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteRequest;
import org.cloudfoundry.client.v2.applications.AssociateApplicationRouteResponse;
import org.cloudfoundry.client.v2.applications.CopyApplicationRequest;
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.DownloadApplicationDropletRequest;
import org.cloudfoundry.client.v2.applications.DownloadApplicationRequest;
import org.cloudfoundry.client.v2.applications.GetApplicationRequest;
import org.cloudfoundry.client.v2.applications.ListApplicationRoutesRequest;
import org.cloudfoundry.client.v2.applications.ListApplicationServiceBindingsRequest;
import org.cloudfoundry.client.v2.applications.ListApplicationsRequest;
import org.cloudfoundry.client.v2.applications.RemoveApplicationRouteRequest;
import org.cloudfoundry.client.v2.applications.RemoveApplicationServiceBindingRequest;
import org.cloudfoundry.client.v2.applications.RestageApplicationRequest;
import org.cloudfoundry.client.v2.applications.SummaryApplicationRequest;
import org.cloudfoundry.client.v2.applications.SummaryApplicationResponse;
import org.cloudfoundry.client.v2.applications.TerminateApplicationInstanceRequest;
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.privatedomains.CreatePrivateDomainRequest;
import org.cloudfoundry.client.v2.privatedomains.CreatePrivateDomainResponse;
import org.cloudfoundry.client.v2.routes.CreateRouteRequest;
import org.cloudfoundry.client.v2.routes.CreateRouteResponse;
import org.cloudfoundry.client.v2.routes.RouteResource;
import org.cloudfoundry.client.v2.servicebindings.CreateServiceBindingRequest;
import org.cloudfoundry.client.v2.servicebindings.CreateServiceBindingResponse;
import org.cloudfoundry.client.v2.servicebindings.ServiceBindingEntity;
import org.cloudfoundry.client.v2.servicebindings.ServiceBindingResource;
import org.cloudfoundry.client.v2.userprovidedserviceinstances.CreateUserProvidedServiceInstanceRequest;
import org.cloudfoundry.client.v2.userprovidedserviceinstances.CreateUserProvidedServiceInstanceResponse;
import org.cloudfoundry.util.DelayUtils;
import org.cloudfoundry.util.JobUtils;
import org.cloudfoundry.util.OperationUtils;
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 org.springframework.util.Assert;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.util.function.Tuple2;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.zip.GZIPInputStream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.cloudfoundry.client.ZipExpectations.zipEquality;
import static org.cloudfoundry.util.OperationUtils.thenKeep;
import static org.cloudfoundry.util.tuple.TupleUtils.consumer;
import static org.cloudfoundry.util.tuple.TupleUtils.function;
public final class ApplicationsTest extends AbstractIntegrationTest {
@Autowired
private CloudFoundryClient cloudFoundryClient;
@Autowired
private Mono<String> organizationId;
@Autowired
private Mono<String> spaceId;
@Autowired
private Mono<String> stackId;
@Test
public void associateRoute() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono
.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
createApplicationId(this.cloudFoundryClient, spaceId, applicationName),
createRouteWithDomain(this.cloudFoundryClient, organizationId, spaceId, domainName, "test-host", "/test/path")
.map(ResourceUtils::getId)
)))
.as(thenKeep(function((applicationId, routeId) -> requestAssociateRoute(this.cloudFoundryClient, applicationId, routeId))))
.then(function((applicationId, routeId) -> Mono.when(
getSingleRouteId(this.cloudFoundryClient, applicationId),
Mono.just(routeId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void copy() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String copyApplicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> Mono.when(
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
.as(thenKeep(applicationId -> uploadApplication(this.cloudFoundryClient, applicationId))),
requestCreateApplication(this.cloudFoundryClient, spaceId, copyApplicationName)
.map(ResourceUtils::getId)
))
.as(thenKeep(function((sourceId, targetId) -> this.cloudFoundryClient.applicationsV2()
.copy(CopyApplicationRequest.builder()
.applicationId(targetId)
.sourceApplicationId(sourceId)
.build())
.then(job -> JobUtils.waitForCompletion(this.cloudFoundryClient, Duration.ofMinutes(5), job))
)))
.then(function((sourceId, targetId) -> Mono.when(
downloadApplication(this.cloudFoundryClient, sourceId),
downloadApplication(this.cloudFoundryClient, targetId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void create() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> Mono.when(
Mono.just(spaceId),
requestCreateApplication(this.cloudFoundryClient, spaceId, applicationName)
.map(ResourceUtils::getEntity)
))
.as(StepVerifier::create)
.consumeNextWith(consumer((spaceId, entity) -> {
assertThat(entity.getSpaceId()).isEqualTo(spaceId);
assertThat(entity.getName()).isEqualTo(applicationName);
}))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void delete() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> this.cloudFoundryClient.applicationsV2()
.delete(DeleteApplicationRequest.builder()
.applicationId(applicationId)
.build())))
.then(applicationId -> requestGetApplication(this.cloudFoundryClient, applicationId))
.as(StepVerifier::create)
.consumeErrorWith(t -> assertThat(t).isInstanceOf(ClientV2Exception.class).hasMessageMatching("CF-AppNotFound\\([0-9]+\\): The app could not be found: .*"))
.verify(Duration.ofMinutes(5));
}
@Test
public void downloadDroplet() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> uploadAndStartApplication(this.cloudFoundryClient, applicationId)))
.flatMapMany(applicationId -> this.cloudFoundryClient.applicationsV2()
.downloadDroplet(DownloadApplicationDropletRequest.builder()
.applicationId(applicationId)
.build())
.as(OperationUtils::collectByteArray))
.as(StepVerifier::create)
.consumeNextWith(isTestApplicationDroplet())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void environment() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> Mono.when(
Mono.just(applicationId),
this.cloudFoundryClient.applicationsV2()
.environment(ApplicationEnvironmentRequest.builder()
.applicationId(applicationId)
.build())
.map(response -> getStringApplicationEnvValue(response.getApplicationEnvironmentJsons(), "VCAP_APPLICATION", "application_id"))
))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void get() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> Mono.when(
Mono.just(applicationId),
requestGetApplication(this.cloudFoundryClient, applicationId)
))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void list() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> Mono.when(
Mono.just(applicationId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.list(ListApplicationsRequest.builder()
.page(page)
.build()))
.filter(resource -> ResourceUtils.getId(resource).equals(applicationId))
.single()
.cast(AbstractApplicationResource.class)
))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listFilterByDiego() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> Mono.when(
Mono.just(applicationId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.list(ListApplicationsRequest.builder()
.diego(true)
.page(page)
.build()))
.filter(resource -> ResourceUtils.getId(resource).equals(applicationId))
.single()
.cast(AbstractApplicationResource.class)
))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listFilterByName() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> Mono.when(
Mono.just(applicationId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.list(ListApplicationsRequest.builder()
.name(applicationName)
.page(page)
.build()))
.filter(resource -> ResourceUtils.getId(resource).equals(applicationId))
.single()
.cast(AbstractApplicationResource.class)
))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listFilterByOrganizationId() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
Mono.when(
this.organizationId,
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
)
.then(function((organizationId, applicationId) -> Mono.when(
Mono.just(applicationId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.list(ListApplicationsRequest.builder()
.organizationId(organizationId)
.page(page)
.build()))
.filter(resource -> ResourceUtils.getId(resource).equals(applicationId))
.single()
.cast(AbstractApplicationResource.class)
)))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listFilterBySpaceId() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> Mono.when(
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
))
.then(function((spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.list(ListApplicationsRequest.builder()
.spaceId(spaceId)
.page(page)
.build()))
.filter(resource -> ResourceUtils.getId(resource).equals(applicationId))
.single()
.cast(AbstractApplicationResource.class)
)))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listFilterByStackId() {
String applicationName = this.nameFactory.getApplicationName();
Mono.when(
this.stackId,
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
)
.then(function((stackId, applicationId) -> Mono.when(
Mono.just(applicationId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.list(ListApplicationsRequest.builder()
.stackId(stackId)
.page(page)
.build()))
.filter(resource -> ResourceUtils.getId(resource).equals(applicationId))
.single()
.cast(AbstractApplicationResource.class)
)))
.as(StepVerifier::create)
.consumeNextWith(applicationIdAndNameEquality(applicationName))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listRoutes() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
Mono.just(organizationId),
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
)))
.then(function((organizationId, spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
createApplicationRoute(this.cloudFoundryClient, organizationId, spaceId, domainName, applicationId)
)))
.then(function((applicationId, routeResponse) -> Mono.when(
Mono.just(ResourceUtils.getId(routeResponse)),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.listRoutes(ListApplicationRoutesRequest.builder()
.applicationId(applicationId)
.page(page)
.build()))
.single()
.map(ResourceUtils::getId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listRoutesFilterByDomainId() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
Mono.just(organizationId),
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
)))
.then(function((organizationId, spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
createApplicationRoute(this.cloudFoundryClient, organizationId, spaceId, domainName, applicationId)
)))
.then(function((applicationId, routeResponse) -> Mono.when(
Mono.just(ResourceUtils.getId(routeResponse)),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.listRoutes(ListApplicationRoutesRequest.builder()
.applicationId(applicationId)
.domainId(ResourceUtils.getEntity(routeResponse).getDomainId())
.page(page)
.build()))
.single()
.map(ResourceUtils::getId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listRoutesFilterByHost() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
Mono.just(organizationId),
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
)))
.then(function((organizationId, spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
createApplicationRoute(this.cloudFoundryClient, organizationId, spaceId, domainName, applicationId)
)))
.then(function((applicationId, routeResponse) -> Mono.when(
Mono.just(ResourceUtils.getId(routeResponse)),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.listRoutes(ListApplicationRoutesRequest.builder()
.applicationId(applicationId)
.host(ResourceUtils.getEntity(routeResponse).getHost())
.page(page)
.build()))
.single()
.map(ResourceUtils::getId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listRoutesFilterByPath() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
Mono.just(organizationId),
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
)))
.then(function((organizationId, spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
createApplicationRoute(this.cloudFoundryClient, organizationId, spaceId, domainName, applicationId)
)))
.then(function((applicationId, routeResponse) -> Mono.when(
Mono.just(ResourceUtils.getId(routeResponse)),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.listRoutes(ListApplicationRoutesRequest.builder()
.applicationId(applicationId)
.path(ResourceUtils.getEntity(routeResponse).getPath())
.page(page)
.build()))
.single()
.map(ResourceUtils::getId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listRoutesFilterByPort() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
Mono.just(organizationId),
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
)))
.then(function((organizationId, spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
createApplicationRoute(this.cloudFoundryClient, organizationId, spaceId, domainName, applicationId)
)))
.then(function((applicationId, routeResponse) -> Mono.when(
Mono.just(ResourceUtils.getId(routeResponse)),
PaginationUtils
.requestClientV2Resources(page -> {
ListApplicationRoutesRequest.Builder builder = ListApplicationRoutesRequest.builder()
.applicationId(applicationId)
.page(page);
Optional.ofNullable(ResourceUtils.getEntity(routeResponse).getPort()).ifPresent(builder::port);
return this.cloudFoundryClient.applicationsV2()
.listRoutes(builder
.build());
})
.single()
.map(ResourceUtils::getId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listServiceBindings() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String serviceInstanceName = this.nameFactory.getServiceInstanceName();
this.spaceId
.then(spaceId -> Mono.when(
createApplicationId(this.cloudFoundryClient, spaceId, applicationName),
createUserServiceInstanceId(this.cloudFoundryClient, spaceId, serviceInstanceName)
))
.as(thenKeep(function((applicationId, serviceInstanceId) -> createServiceBindingId(this.cloudFoundryClient, applicationId, serviceInstanceId))))
.then(function((applicationId, serviceInstanceId) -> Mono.when(
Mono.just(serviceInstanceId),
getSingleServiceBindingInstanceId(this.cloudFoundryClient, applicationId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void listServiceBindingsFilterByServiceInstanceId() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String serviceInstanceName = this.nameFactory.getServiceInstanceName();
this.spaceId
.then(spaceId -> Mono.when(
createApplicationId(this.cloudFoundryClient, spaceId, applicationName),
createUserServiceInstanceId(this.cloudFoundryClient, spaceId, serviceInstanceName)
))
.then(function((applicationId, serviceInstanceId) -> Mono.when(
Mono.just(applicationId),
Mono.just(serviceInstanceId),
createServiceBindingId(this.cloudFoundryClient, applicationId, serviceInstanceId)
)))
.then(function((applicationId, serviceInstanceId, serviceBindingId) -> Mono.when(
Mono.just(serviceBindingId),
PaginationUtils
.requestClientV2Resources(page -> this.cloudFoundryClient.applicationsV2()
.listServiceBindings(ListApplicationServiceBindingsRequest.builder()
.applicationId(applicationId)
.serviceInstanceId(serviceInstanceId)
.page(page)
.build()))
.single()
.map(ResourceUtils::getId)
)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void removeRoute() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String domainName = this.nameFactory.getDomainName();
Mono.when(this.organizationId, this.spaceId)
.then(function((organizationId, spaceId) -> Mono.when(
Mono.just(organizationId),
Mono.just(spaceId),
createApplicationId(this.cloudFoundryClient, spaceId, applicationName)
)))
.then(function((organizationId, spaceId, applicationId) -> Mono.when(
Mono.just(applicationId),
createApplicationRoute(this.cloudFoundryClient, organizationId, spaceId, domainName, applicationId)
)))
.as(thenKeep(function((applicationId, routeResponse) -> this.cloudFoundryClient.applicationsV2()
.removeRoute(RemoveApplicationRouteRequest.builder()
.applicationId(applicationId)
.routeId(ResourceUtils.getId(routeResponse))
.build()))))
.flatMapMany(function((applicationId, routeResponse) -> requestRoutes(this.cloudFoundryClient, applicationId)))
.as(StepVerifier::create)
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void removeServiceBinding() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String serviceInstanceName = this.nameFactory.getServiceInstanceName();
this.spaceId
.then(spaceId -> Mono.when(
createApplicationId(this.cloudFoundryClient, spaceId, applicationName),
createUserServiceInstanceId(this.cloudFoundryClient, spaceId, serviceInstanceName)
))
.then(function((applicationId, serviceInstanceId) -> Mono.when(
Mono.just(applicationId),
createServiceBindingId(this.cloudFoundryClient, applicationId, serviceInstanceId)
)))
.as(thenKeep(function((applicationId, serviceBindingId) -> this.cloudFoundryClient.applicationsV2()
.removeServiceBinding(RemoveApplicationServiceBindingRequest.builder()
.applicationId(applicationId)
.serviceBindingId(serviceBindingId)
.build()))))
.flatMapMany(function((applicationId, serviceBindingId) -> requestServiceBindings(this.cloudFoundryClient, applicationId)))
.as(StepVerifier::create)
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void restage() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> uploadAndStartApplication(this.cloudFoundryClient, applicationId)))
.as(thenKeep(applicationId -> this.cloudFoundryClient.applicationsV2()
.restage(RestageApplicationRequest.builder()
.applicationId(applicationId)
.build())))
.then(applicationId -> waitForStagingApplication(this.cloudFoundryClient, applicationId))
.map(resource -> ResourceUtils.getEntity(resource).getName())
.as(StepVerifier::create)
.expectNext(applicationName)
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void statistics() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> uploadAndStartApplication(this.cloudFoundryClient, applicationId)))
.then(applicationId -> this.cloudFoundryClient.applicationsV2()
.statistics(ApplicationStatisticsRequest.builder()
.applicationId(applicationId)
.build())
.map(instanceStatistics -> instanceStatistics.getInstances().get("0").getStatistics().getName()))
.as(StepVerifier::create)
.expectNext(applicationName)
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void summary() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> this.cloudFoundryClient.applicationsV2()
.summary(SummaryApplicationRequest.builder()
.applicationId(applicationId)
.build())
.map(SummaryApplicationResponse::getId)
.and(Mono.just(applicationId)))
.as(StepVerifier::create)
.consumeNextWith(tupleEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void terminateInstance() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> uploadAndStartApplication(this.cloudFoundryClient, applicationId)))
.then(applicationId -> Mono.when(
Mono.just(applicationId),
getInstanceInfo(this.cloudFoundryClient, applicationId, "0")
.map(info -> Optional.ofNullable(info.getSince()))
))
.as(thenKeep(function((applicationId, optionalSince) -> this.cloudFoundryClient.applicationsV2()
.terminateInstance(TerminateApplicationInstanceRequest.builder()
.applicationId(applicationId)
.index("0")
.build()))))
.then(function((applicationId, optionalSince) -> waitForInstanceRestart(this.cloudFoundryClient, applicationId, "0", optionalSince)))
.as(StepVerifier::create)
.expectNextCount(1)
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void update() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
String applicationName2 = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> this.cloudFoundryClient.applicationsV2()
.update(UpdateApplicationRequest.builder()
.applicationId(applicationId)
.name(applicationName2)
.environmentJson("test-var", "test-value")
.build())
.map(ResourceUtils::getId))
.then(applicationId ->
Mono.when(
Mono.just(applicationId),
requestGetApplication(this.cloudFoundryClient, applicationId)
.map(ResourceUtils::getEntity)
))
.then(function((applicationId, entity1) ->
Mono.when(
Mono.just(entity1),
this.cloudFoundryClient.applicationsV2()
.update(UpdateApplicationRequest.builder()
.applicationId(applicationId)
.environmentJsons(Collections.emptyMap())
.build())
.then(requestGetApplication(this.cloudFoundryClient, applicationId)
.map(ResourceUtils::getEntity))
)))
.as(StepVerifier::create)
.consumeNextWith(consumer((entity1, entity2) -> {
assertThat(entity1.getName()).isEqualTo(applicationName2);
assertThat(entity1.getEnvironmentJsons()).containsEntry("test-var", "test-value");
assertThat(entity2.getEnvironmentJsons()).isEmpty();
}))
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void uploadAndDownload() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> uploadApplication(this.cloudFoundryClient, applicationId)))
.then(applicationId -> Mono.when(
downloadApplication(this.cloudFoundryClient, applicationId),
getBytes("test-application.zip")
))
.as(StepVerifier::create)
.consumeNextWith(zipEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void uploadAndDownloadAsyncFalse() throws TimeoutException, InterruptedException {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.as(thenKeep(applicationId -> uploadApplicationAsyncFalse(this.cloudFoundryClient, applicationId)))
.then(applicationId -> Mono.when(
downloadApplication(this.cloudFoundryClient, applicationId),
getBytes("test-application.zip")
))
.as(StepVerifier::create)
.consumeNextWith(zipEquality())
.expectComplete()
.verify(Duration.ofMinutes(5));
}
@Test
public void uploadDirectory() {
String applicationName = this.nameFactory.getApplicationName();
this.spaceId
.then(spaceId -> createApplicationId(this.cloudFoundryClient, spaceId, applicationName))
.then(applicationId -> {
try {
return this.cloudFoundryClient.applicationsV2()
.upload(UploadApplicationRequest.builder()
.application(new ClassPathResource("test-application").getFile().toPath())
.async(true)
.applicationId(applicationId)
.build())
.then(job -> JobUtils.waitForCompletion(this.cloudFoundryClient, Duration.ofMinutes(5), job));
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.as(StepVerifier::create)
.expectComplete()
.verify(Duration.ofMinutes(5));
}
private static Consumer<Tuple2<String, AbstractApplicationResource>> applicationIdAndNameEquality(String name) {
Assert.notNull(name, "name must not be null");
return consumer((applicationId, resource) -> {
assertThat(ResourceUtils.getId(resource)).isEqualTo(applicationId);
assertThat(ResourceUtils.getEntity(resource).getName()).isEqualTo(name);
});
}
private static Mono<String> createApplicationId(CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName) {
return requestCreateApplication(cloudFoundryClient, spaceId, applicationName, "staticfile_buildpack", true, 512, 64)
.map(ResourceUtils::getId);
}
private static Mono<CreateRouteResponse> createApplicationRoute(CloudFoundryClient cloudFoundryClient, String organizationId, String spaceId, String domainName, String applicationId) {
return createRouteWithDomain(cloudFoundryClient, organizationId, spaceId, domainName, "test-host", "/test-path")
.then(createRouteResponse -> requestAssociateRoute(cloudFoundryClient, applicationId, createRouteResponse.getMetadata().getId())
.map(response -> createRouteResponse));
}
private static Mono<String> createPrivateDomainId(CloudFoundryClient cloudFoundryClient, String name, String organizationId) {
return requestCreatePrivateDomain(cloudFoundryClient, name, organizationId)
.map(ResourceUtils::getId);
}
private static Mono<CreateRouteResponse> createRouteWithDomain(CloudFoundryClient cloudFoundryClient, String organizationId, String spaceId, String domainName, String host, String path) {
return createPrivateDomainId(cloudFoundryClient, domainName, organizationId)
.then(domainId -> cloudFoundryClient.routes()
.create(CreateRouteRequest.builder()
.domainId(domainId)
.host(host)
.path(path)
.spaceId(spaceId)
.build()));
}
private static Mono<String> createServiceBindingId(CloudFoundryClient cloudFoundryClient, String applicationId, String serviceInstanceId) {
return requestCreateServiceBinding(cloudFoundryClient, applicationId, serviceInstanceId)
.map(ResourceUtils::getId);
}
private static Mono<String> createUserServiceInstanceId(CloudFoundryClient cloudFoundryClient, String spaceId, String serviceInstanceName) {
return requestCreateUserServiceInstance(cloudFoundryClient, spaceId, serviceInstanceName)
.map(ResourceUtils::getId);
}
private static Mono<ApplicationInstanceInfo> getInstanceInfo(CloudFoundryClient cloudFoundryClient, String applicationId, String instanceName) {
return requestInstances(cloudFoundryClient, applicationId)
.filter(response -> response.getInstances().containsKey(instanceName))
.map(response -> response.getInstances().get(instanceName));
}
private static Mono<String> getSingleRouteId(CloudFoundryClient cloudFoundryClient, String applicationId) {
return requestRoutes(cloudFoundryClient, applicationId)
.single()
.map(ResourceUtils::getId);
}
private static Mono<String> getSingleServiceBindingInstanceId(CloudFoundryClient cloudFoundryClient, String applicationId) {
return requestServiceBindings(cloudFoundryClient, applicationId)
.single()
.map(ResourceUtils::getEntity)
.map(ServiceBindingEntity::getServiceInstanceId);
}
@SuppressWarnings("unchecked")
private static String getStringApplicationEnvValue(Map<String, Object> environment, String... keys) {
for (int i = 0; i < keys.length - 1; ++i) {
environment = (Map<String, Object>) environment.get(keys[i]);
}
return (String) environment.get(keys[keys.length - 1]);
}
private static boolean isIdentical(Double expected, Double actual) {
return expected == null ? actual == null : expected.equals(actual);
}
private static Consumer<byte[]> isTestApplicationDroplet() {
return bytes -> {
Set<String> names = new HashSet<>();
try (TarArchiveInputStream in = new TarArchiveInputStream(new GZIPInputStream(new ByteArrayInputStream(bytes)))) {
TarArchiveEntry entry;
while ((entry = in.getNextTarEntry()) != null) {
names.add(entry.getName());
}
} catch (IOException e) {
throw Exceptions.propagate(e);
}
assertThat(names).contains("./app/Staticfile", "./app/public/index.html");
};
}
private static Mono<AssociateApplicationRouteResponse> requestAssociateRoute(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 requestCreateApplication(cloudFoundryClient, spaceId, applicationName, null, null, null, null);
}
private static Mono<CreateApplicationResponse> requestCreateApplication(CloudFoundryClient cloudFoundryClient, String spaceId, String applicationName, String buildpack, Boolean diego, Integer
diskQuota, Integer memory) {
return cloudFoundryClient.applicationsV2()
.create(CreateApplicationRequest.builder()
.buildpack(buildpack)
.diego(diego)
.diskQuota(diskQuota)
.memory(memory)
.name(applicationName)
.spaceId(spaceId)
.build());
}
private static Mono<CreatePrivateDomainResponse> requestCreatePrivateDomain(CloudFoundryClient cloudFoundryClient, String name, String organizationId) {
return cloudFoundryClient.privateDomains()
.create(CreatePrivateDomainRequest.builder()
.name(name)
.owningOrganizationId(organizationId)
.build());
}
private static Mono<CreateServiceBindingResponse> requestCreateServiceBinding(CloudFoundryClient cloudFoundryClient, String applicationId, String serviceInstanceId) {
return cloudFoundryClient.serviceBindingsV2()
.create(CreateServiceBindingRequest.builder()
.applicationId(applicationId)
.serviceInstanceId(serviceInstanceId)
.build());
}
private static Mono<CreateUserProvidedServiceInstanceResponse> requestCreateUserServiceInstance(CloudFoundryClient cloudFoundryClient, String spaceId, String name) {
return cloudFoundryClient.userProvidedServiceInstances()
.create(CreateUserProvidedServiceInstanceRequest.builder()
.name(name)
.spaceId(spaceId)
.build());
}
private static Mono<AbstractApplicationResource> requestGetApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
return cloudFoundryClient.applicationsV2()
.get(GetApplicationRequest.builder()
.applicationId(applicationId)
.build())
.cast(AbstractApplicationResource.class);
}
private static Mono<ApplicationInstancesResponse> requestInstances(CloudFoundryClient cloudFoundryClient, String applicationId) {
return cloudFoundryClient.applicationsV2()
.instances(ApplicationInstancesRequest.builder()
.applicationId(applicationId)
.build());
}
private static Flux<RouteResource> requestRoutes(CloudFoundryClient cloudFoundryClient, String applicationId) {
return PaginationUtils.requestClientV2Resources(page -> cloudFoundryClient.applicationsV2()
.listRoutes(ListApplicationRoutesRequest.builder()
.page(page)
.applicationId(applicationId)
.build()));
}
private static Flux<ServiceBindingResource> requestServiceBindings(CloudFoundryClient cloudFoundryClient, String applicationId) {
return PaginationUtils
.requestClientV2Resources(page -> cloudFoundryClient.applicationsV2()
.listServiceBindings(ListApplicationServiceBindingsRequest.builder()
.applicationId(applicationId)
.page(page)
.build()));
}
private static Mono<UpdateApplicationResponse> requestUpdateApplicationState(CloudFoundryClient cloudFoundryClient, String applicationId, String state) {
return cloudFoundryClient.applicationsV2()
.update(UpdateApplicationRequest.builder()
.applicationId(applicationId)
.state(state)
.build());
}
private static Mono<ApplicationInstanceInfo> startApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
return requestUpdateApplicationState(cloudFoundryClient, applicationId, "STARTED")
.then(waitForStagingApplication(cloudFoundryClient, applicationId))
.then(waitForStartingInstanceInfo(cloudFoundryClient, applicationId));
}
private static Mono<ApplicationInstanceInfo> uploadAndStartApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
return uploadApplication(cloudFoundryClient, applicationId)
.then(startApplication(cloudFoundryClient, applicationId));
}
private static Mono<Void> uploadApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
try {
return cloudFoundryClient.applicationsV2()
.upload(UploadApplicationRequest.builder()
.application(new ClassPathResource("test-application.zip").getFile().toPath())
.async(true)
.applicationId(applicationId)
.build())
.then(job -> JobUtils.waitForCompletion(cloudFoundryClient, Duration.ofMinutes(5), job));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static Mono<UploadApplicationResponse> uploadApplicationAsyncFalse(CloudFoundryClient cloudFoundryClient, String applicationId) {
try {
return cloudFoundryClient.applicationsV2()
.upload(UploadApplicationRequest.builder()
.application(new ClassPathResource("test-application.zip").getFile().toPath())
.async(false)
.applicationId(applicationId)
.build());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static Mono<ApplicationInstanceInfo> waitForInstanceRestart(CloudFoundryClient cloudFoundryClient, String applicationId, String instanceName, Optional<Double> optionalSince) {
return getInstanceInfo(cloudFoundryClient, applicationId, instanceName)
.filter(info -> !isIdentical(info.getSince(), optionalSince.orElse(null)))
.repeatWhenEmpty(DelayUtils.exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5)));
}
private static Mono<AbstractApplicationResource> waitForStagingApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
return requestGetApplication(cloudFoundryClient, applicationId)
.filter(response -> "STAGED".equals(response.getEntity().getPackageState()))
.repeatWhenEmpty(DelayUtils.exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5)));
}
private static Mono<ApplicationInstanceInfo> waitForStartingInstanceInfo(CloudFoundryClient cloudFoundryClient, String applicationId) {
return cloudFoundryClient.applicationsV2()
.instances(ApplicationInstancesRequest.builder()
.applicationId(applicationId)
.build())
.flatMapMany(response -> Flux.fromIterable(response.getInstances().values()))
.filter(applicationInstanceInfo -> "RUNNING".equals(applicationInstanceInfo.getState()))
.next()
.repeatWhenEmpty(DelayUtils.exponentialBackOff(Duration.ofSeconds(1), Duration.ofSeconds(15), Duration.ofMinutes(5)));
}
private Mono<byte[]> downloadApplication(CloudFoundryClient cloudFoundryClient, String applicationId) {
return cloudFoundryClient.applicationsV2()
.download(DownloadApplicationRequest.builder()
.applicationId(applicationId)
.build())
.as(OperationUtils::collectByteArray);
}
}