/*
* 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.organizationadmin;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.organizationquotadefinitions.AbstractOrganizationQuotaDefinition;
import org.cloudfoundry.client.v2.organizationquotadefinitions.CreateOrganizationQuotaDefinitionRequest;
import org.cloudfoundry.client.v2.organizationquotadefinitions.CreateOrganizationQuotaDefinitionResponse;
import org.cloudfoundry.client.v2.organizationquotadefinitions.DeleteOrganizationQuotaDefinitionRequest;
import org.cloudfoundry.client.v2.organizationquotadefinitions.DeleteOrganizationQuotaDefinitionResponse;
import org.cloudfoundry.client.v2.organizationquotadefinitions.ListOrganizationQuotaDefinitionsRequest;
import org.cloudfoundry.client.v2.organizationquotadefinitions.OrganizationQuotaDefinitionEntity;
import org.cloudfoundry.client.v2.organizationquotadefinitions.OrganizationQuotaDefinitionResource;
import org.cloudfoundry.client.v2.organizationquotadefinitions.UpdateOrganizationQuotaDefinitionRequest;
import org.cloudfoundry.client.v2.organizationquotadefinitions.UpdateOrganizationQuotaDefinitionResponse;
import org.cloudfoundry.client.v2.organizations.ListOrganizationsRequest;
import org.cloudfoundry.client.v2.organizations.OrganizationResource;
import org.cloudfoundry.client.v2.organizations.UpdateOrganizationRequest;
import org.cloudfoundry.client.v2.organizations.UpdateOrganizationResponse;
import org.cloudfoundry.operations.util.OperationsLogging;
import org.cloudfoundry.util.ExceptionUtils;
import org.cloudfoundry.util.JobUtils;
import org.cloudfoundry.util.PaginationUtils;
import org.cloudfoundry.util.ResourceUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.NoSuchElementException;
import java.util.Optional;
import static org.cloudfoundry.util.tuple.TupleUtils.function;
public final class DefaultOrganizationAdmin implements OrganizationAdmin {
private final Mono<CloudFoundryClient> cloudFoundryClient;
public DefaultOrganizationAdmin(Mono<CloudFoundryClient> cloudFoundryClient) {
this.cloudFoundryClient = cloudFoundryClient;
}
@Override
public Mono<OrganizationQuota> createQuota(CreateQuotaRequest request) {
return this.cloudFoundryClient
.then(cloudFoundryClient -> createOrganizationQuota(cloudFoundryClient, request))
.map(DefaultOrganizationAdmin::toOrganizationQuota)
.transform(OperationsLogging.log("Create Organization Quota"))
.checkpoint();
}
@Override
public Mono<Void> deleteQuota(DeleteQuotaRequest request) {
return this.cloudFoundryClient
.then(cloudFoundryClient -> Mono.when(
Mono.just(cloudFoundryClient),
Mono.just(request.getCompletionTimeout()),
getOrganizationQuotaId(cloudFoundryClient, request.getName())
))
.then(function(DefaultOrganizationAdmin::deleteOrganizationQuota))
.transform(OperationsLogging.log("Delete Organization Quota"))
.checkpoint();
}
@Override
public Mono<OrganizationQuota> getQuota(GetQuotaRequest request) {
return this.cloudFoundryClient
.then(cloudFoundryClient -> getOrganizationQuota(cloudFoundryClient, request.getName()))
.map(DefaultOrganizationAdmin::toOrganizationQuota)
.transform(OperationsLogging.log("Get Organization Quota"))
.checkpoint();
}
@Override
public Flux<OrganizationQuota> listQuotas() {
return this.cloudFoundryClient
.flatMapMany(DefaultOrganizationAdmin::requestListOrganizationQuotas)
.map(DefaultOrganizationAdmin::toOrganizationQuota)
.transform(OperationsLogging.log("List Organization Quotas"))
.checkpoint();
}
@Override
public Mono<Void> setQuota(SetQuotaRequest request) {
return this.cloudFoundryClient
.then(cloudFoundryClient -> Mono.when(
Mono.just(cloudFoundryClient),
getOrganizationId(cloudFoundryClient, request.getOrganizationName()),
getOrganizationQuotaId(cloudFoundryClient, request.getQuotaName())
))
.then(function((DefaultOrganizationAdmin::requestUpdateOrganization)))
.then()
.transform(OperationsLogging.log("Set Organization Quota"))
.checkpoint();
}
@Override
public Mono<OrganizationQuota> updateQuota(UpdateQuotaRequest request) {
return this.cloudFoundryClient
.then(cloudFoundryClient -> Mono.when(
Mono.just(cloudFoundryClient),
getOrganizationQuota(cloudFoundryClient, request.getName())
))
.then(function((cloudFoundryClient, exitingQuotaDefinition) -> updateOrganizationQuota(cloudFoundryClient, request, exitingQuotaDefinition)))
.map(DefaultOrganizationAdmin::toOrganizationQuota)
.transform(OperationsLogging.log("Update Organization Quota"))
.checkpoint();
}
private static Mono<CreateOrganizationQuotaDefinitionResponse> createOrganizationQuota(CloudFoundryClient cloudFoundryClient, CreateQuotaRequest request) {
return requestCreateOrganizationQuota(
cloudFoundryClient,
Optional.ofNullable(request.getInstanceMemoryLimit()).orElse(-1),
Optional.ofNullable(request.getMemoryLimit()).orElse(0),
request.getName(),
Optional.ofNullable(request.getAllowPaidServicePlans()).orElse(false),
Optional.ofNullable(request.getTotalRoutes()).orElse(0),
Optional.ofNullable(request.getTotalServices()).orElse(0));
}
private static Mono<Void> deleteOrganizationQuota(CloudFoundryClient cloudFoundryClient, Duration completionTimeout, String quotaId) {
return requestDeleteOrganizationQuota(cloudFoundryClient, quotaId)
.then(job -> JobUtils.waitForCompletion(cloudFoundryClient, completionTimeout, job));
}
private static Mono<String> getOrganizationId(CloudFoundryClient cloudFoundryClient, String name) {
return requestListOrganizations(cloudFoundryClient, name)
.single()
.onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Organization %s does not exist", name))
.map(ResourceUtils::getId);
}
private static Mono<OrganizationQuotaDefinitionResource> getOrganizationQuota(CloudFoundryClient cloudFoundryClient, String name) {
return requestListOrganizationQuotas(cloudFoundryClient, name)
.single()
.onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Quota %s does not exist", name));
}
private static Mono<String> getOrganizationQuotaId(CloudFoundryClient cloudFoundryClient, String name) {
return getOrganizationQuota(cloudFoundryClient, name)
.map(ResourceUtils::getId);
}
private static Mono<CreateOrganizationQuotaDefinitionResponse> requestCreateOrganizationQuota(CloudFoundryClient cloudFoundryClient, Integer instanceMemoryLimit, Integer memoryLimit, String name,
Boolean nonBasicServicesAllowed, Integer totalRoutes, Integer totalServices) {
return cloudFoundryClient.organizationQuotaDefinitions()
.create(CreateOrganizationQuotaDefinitionRequest.builder()
.instanceMemoryLimit(instanceMemoryLimit)
.memoryLimit(memoryLimit)
.name(name)
.nonBasicServicesAllowed(nonBasicServicesAllowed)
.totalRoutes(totalRoutes)
.totalServices(totalServices)
.build());
}
private static Mono<DeleteOrganizationQuotaDefinitionResponse> requestDeleteOrganizationQuota(CloudFoundryClient cloudFoundryClient, String quotaId) {
return cloudFoundryClient.organizationQuotaDefinitions()
.delete(DeleteOrganizationQuotaDefinitionRequest.builder()
.organizationQuotaDefinitionId(quotaId)
.async(true)
.build());
}
private static Flux<OrganizationQuotaDefinitionResource> requestListOrganizationQuotas(CloudFoundryClient cloudFoundryClient) {
return PaginationUtils
.requestClientV2Resources(page -> cloudFoundryClient.organizationQuotaDefinitions()
.list(ListOrganizationQuotaDefinitionsRequest.builder()
.page(page)
.build()));
}
private static Flux<OrganizationQuotaDefinitionResource> requestListOrganizationQuotas(CloudFoundryClient cloudFoundryClient, String name) {
return PaginationUtils
.requestClientV2Resources(page -> cloudFoundryClient.organizationQuotaDefinitions()
.list(ListOrganizationQuotaDefinitionsRequest.builder()
.name(name)
.page(page)
.build()));
}
private static Flux<OrganizationResource> requestListOrganizations(CloudFoundryClient cloudFoundryClient, String name) {
return PaginationUtils
.requestClientV2Resources(page -> cloudFoundryClient.organizations()
.list(ListOrganizationsRequest.builder()
.name(name)
.page(page)
.build()));
}
private static Mono<UpdateOrganizationResponse> requestUpdateOrganization(CloudFoundryClient cloudFoundryClient, String organizationId, String quotaDefinitionId) {
return cloudFoundryClient.organizations()
.update(UpdateOrganizationRequest.builder()
.organizationId(organizationId)
.quotaDefinitionId(quotaDefinitionId)
.build());
}
private static Mono<UpdateOrganizationQuotaDefinitionResponse> requestUpdateOrganizationQuota(CloudFoundryClient cloudFoundryClient, String organizationQuotaDefinitionId,
Integer instanceMemoryLimit, Integer memoryLimit, String name, Boolean nonBasicServicesAllowed,
Integer totalRoutes, Integer totalServices) {
return cloudFoundryClient.organizationQuotaDefinitions()
.update(UpdateOrganizationQuotaDefinitionRequest.builder()
.instanceMemoryLimit(instanceMemoryLimit)
.memoryLimit(memoryLimit)
.name(name)
.organizationQuotaDefinitionId(organizationQuotaDefinitionId)
.nonBasicServicesAllowed(nonBasicServicesAllowed)
.totalRoutes(totalRoutes)
.totalServices(totalServices)
.build());
}
private static OrganizationQuota toOrganizationQuota(AbstractOrganizationQuotaDefinition resource) {
OrganizationQuotaDefinitionEntity entity = ResourceUtils.getEntity(resource);
return OrganizationQuota.builder()
.allowPaidServicePlans(entity.getNonBasicServicesAllowed())
.applicationInstanceLimit(entity.getApplicationInstanceLimit())
.id(ResourceUtils.getId(resource))
.instanceMemoryLimit(entity.getInstanceMemoryLimit())
.memoryLimit(entity.getMemoryLimit())
.name(entity.getName())
.totalRoutes(entity.getTotalRoutes())
.totalServices(entity.getTotalServices())
.build();
}
private static Mono<UpdateOrganizationQuotaDefinitionResponse> updateOrganizationQuota(CloudFoundryClient cloudFoundryClient, UpdateQuotaRequest request,
OrganizationQuotaDefinitionResource resource) {
OrganizationQuotaDefinitionEntity existing = ResourceUtils.getEntity(resource);
return requestUpdateOrganizationQuota(
cloudFoundryClient,
ResourceUtils.getId(resource),
Optional.ofNullable(request.getInstanceMemoryLimit()).orElse(existing.getInstanceMemoryLimit()),
Optional.ofNullable(request.getMemoryLimit()).orElse(existing.getMemoryLimit()),
Optional.ofNullable(request.getNewName()).orElse(existing.getName()),
Optional.ofNullable(request.getAllowPaidServicePlans()).orElse(existing.getNonBasicServicesAllowed()),
Optional.ofNullable(request.getTotalRoutes()).orElse(existing.getTotalRoutes()),
Optional.ofNullable(request.getTotalServices()).orElse(existing.getTotalServices()));
}
}