/* * 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.organizations; import org.cloudfoundry.client.CloudFoundryClient; import org.cloudfoundry.client.v2.featureflags.GetFeatureFlagRequest; import org.cloudfoundry.client.v2.featureflags.GetFeatureFlagResponse; import org.cloudfoundry.client.v2.organizationquotadefinitions.GetOrganizationQuotaDefinitionRequest; import org.cloudfoundry.client.v2.organizationquotadefinitions.GetOrganizationQuotaDefinitionResponse; import org.cloudfoundry.client.v2.organizationquotadefinitions.ListOrganizationQuotaDefinitionsRequest; import org.cloudfoundry.client.v2.organizationquotadefinitions.OrganizationQuotaDefinitionResource; import org.cloudfoundry.client.v2.organizations.AssociateOrganizationManagerByUsernameRequest; import org.cloudfoundry.client.v2.organizations.AssociateOrganizationManagerByUsernameResponse; import org.cloudfoundry.client.v2.organizations.AssociateOrganizationUserByUsernameRequest; import org.cloudfoundry.client.v2.organizations.AssociateOrganizationUserByUsernameResponse; import org.cloudfoundry.client.v2.organizations.CreateOrganizationResponse; import org.cloudfoundry.client.v2.organizations.DeleteOrganizationResponse; import org.cloudfoundry.client.v2.organizations.ListOrganizationPrivateDomainsRequest; import org.cloudfoundry.client.v2.organizations.ListOrganizationSpaceQuotaDefinitionsRequest; import org.cloudfoundry.client.v2.organizations.ListOrganizationSpacesRequest; 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.client.v2.privatedomains.PrivateDomainResource; import org.cloudfoundry.client.v2.shareddomains.ListSharedDomainsRequest; import org.cloudfoundry.client.v2.shareddomains.SharedDomainResource; import org.cloudfoundry.client.v2.spacequotadefinitions.SpaceQuotaDefinitionResource; import org.cloudfoundry.client.v2.spaces.SpaceResource; import org.cloudfoundry.operations.spaceadmin.SpaceQuota; 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 reactor.util.function.Tuple4; import java.time.Duration; import java.util.List; import java.util.NoSuchElementException; import static org.cloudfoundry.util.tuple.TupleUtils.function; import static org.cloudfoundry.util.tuple.TupleUtils.predicate; public final class DefaultOrganizations implements Organizations { private static final String SET_ROLES_BY_USERNAME_FEATURE_FLAG = "set_roles_by_username"; private final Mono<CloudFoundryClient> cloudFoundryClient; private final Mono<String> username; public DefaultOrganizations(Mono<CloudFoundryClient> cloudFoundryClient, Mono<String> username) { this.cloudFoundryClient = cloudFoundryClient; this.username = username; } @Override public Mono<Void> create(CreateOrganizationRequest request) { return Mono .when(this.cloudFoundryClient, this.username) .then(function((cloudFoundryClient, username) -> Mono.when( Mono.just(cloudFoundryClient), createOrganization(cloudFoundryClient, request), getFeatureFlagEnabled(cloudFoundryClient, SET_ROLES_BY_USERNAME_FEATURE_FLAG), Mono.just(username) ))) .filter(predicate((cloudFoundryClient, organizationId, setRolesByUsernameEnabled, username) -> setRolesByUsernameEnabled)) .then(function((cloudFoundryClient, organizationId, setRolesByUsernameEnabled, username) -> setOrganizationManager(cloudFoundryClient, organizationId, username))) .transform(OperationsLogging.log("Create Organization")) .checkpoint(); } @Override public Mono<Void> delete(DeleteOrganizationRequest request) { return this.cloudFoundryClient .then(cloudFoundryClient -> Mono.when( Mono.just(cloudFoundryClient), Mono.just(request.getCompletionTimeout()), getOrganizationId(cloudFoundryClient, request.getName()) )) .then(function(DefaultOrganizations::deleteOrganization)) .transform(OperationsLogging.log("Delete Organization")) .checkpoint(); } @Override public Mono<OrganizationDetail> get(OrganizationInfoRequest request) { return this.cloudFoundryClient .then(cloudFoundryClient -> Mono.when( Mono.just(cloudFoundryClient), getOrganization(cloudFoundryClient, request.getName()) )) .then(function((cloudFoundryClient, organizationResource) -> getAuxiliaryContent(cloudFoundryClient, organizationResource) .map(function((domains, organizationQuota, spacesQuotas, spaces) -> toOrganizationDetail(domains, organizationQuota, spacesQuotas, spaces, organizationResource, request))))) .transform(OperationsLogging.log("Get Organization")) .checkpoint(); } @Override public Flux<OrganizationSummary> list() { return this.cloudFoundryClient .flatMapMany(DefaultOrganizations::requestOrganizations) .map(DefaultOrganizations::toOrganizationSummary) .transform(OperationsLogging.log("List Organizations")) .checkpoint(); } @Override public Mono<Void> rename(RenameOrganizationRequest request) { return this.cloudFoundryClient .then(cloudFoundryClient -> Mono.when( Mono.just(cloudFoundryClient), getOrganizationId(cloudFoundryClient, request.getName()) )) .then(function((cloudFoundryClient, organizationId) -> requestUpdateOrganization(cloudFoundryClient, organizationId, request.getNewName()))) .then() .transform(OperationsLogging.log("Rename Organization")) .checkpoint(); } private static Mono<String> createOrganization(CloudFoundryClient cloudFoundryClient, CreateOrganizationRequest request) { return Mono .justOrEmpty(request.getQuotaDefinitionName()) .then(quotaDefinitionName -> getOrganizationQuotaDefinitionId(cloudFoundryClient, quotaDefinitionName)) .then(organizationQuotaDefinitionId -> getCreateOrganizationId(cloudFoundryClient, request.getOrganizationName(), organizationQuotaDefinitionId)) .switchIfEmpty(getCreateOrganizationId(cloudFoundryClient, request.getOrganizationName(), null)); } private static Mono<Void> deleteOrganization(CloudFoundryClient cloudFoundryClient, Duration completionTimeout, String organizationId) { return requestDeleteOrganization(cloudFoundryClient, organizationId) .then(job -> JobUtils.waitForCompletion(cloudFoundryClient, completionTimeout, job)); } private static Mono<Tuple4<List<String>, OrganizationQuota, List<SpaceQuota>, List<String>>> getAuxiliaryContent(CloudFoundryClient cloudFoundryClient, OrganizationResource organizationResource) { String organizationId = ResourceUtils.getId(organizationResource); return Mono .when( getDomainNames(cloudFoundryClient, organizationId), getOrganizationQuota(cloudFoundryClient, organizationResource), getSpaceQuotas(cloudFoundryClient, organizationId), getSpaceNames(cloudFoundryClient, organizationId) ); } private static Mono<String> getCreateOrganizationId(CloudFoundryClient cloudFoundryClient, String organization, String quotaDefinitionId) { return requestCreateOrganization(cloudFoundryClient, organization, quotaDefinitionId) .map(ResourceUtils::getId); } private static Mono<List<String>> getDomainNames(CloudFoundryClient cloudFoundryClient, String organizationId) { return requestListPrivateDomains(cloudFoundryClient, organizationId) .map(resource -> resource.getEntity().getName()) .mergeWith(requestListSharedDomains(cloudFoundryClient) .map(resource -> resource.getEntity().getName())) .collectList(); } private static Mono<Boolean> getFeatureFlagEnabled(CloudFoundryClient cloudFoundryClient, String featureFlag) { return requestGetFeatureFlag(cloudFoundryClient, featureFlag) .map(GetFeatureFlagResponse::getEnabled); } private static Mono<OrganizationResource> getOrganization(CloudFoundryClient cloudFoundryClient, String organization) { return requestOrganizations(cloudFoundryClient, organization) .single() .onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Organization %s does not exist", organization)); } private static Mono<String> getOrganizationId(CloudFoundryClient cloudFoundryClient, String organization) { return getOrganization(cloudFoundryClient, organization) .map(ResourceUtils::getId); } private static Mono<OrganizationQuota> getOrganizationQuota(CloudFoundryClient cloudFoundryClient, OrganizationResource resource) { return requestOrganizationQuotaDefinition(cloudFoundryClient, ResourceUtils.getEntity(resource).getQuotaDefinitionId()) .map(response -> toOrganizationQuota(response, resource)); } private static Mono<OrganizationQuotaDefinitionResource> getOrganizationQuotaDefinition(CloudFoundryClient cloudFoundryClient, String quotaDefinitionName) { return requestOrganizationQuotaDefinitions(cloudFoundryClient, quotaDefinitionName) .single() .onErrorResume(NoSuchElementException.class, t -> ExceptionUtils.illegalArgument("Organization quota %s does not exist", quotaDefinitionName)); } private static Mono<String> getOrganizationQuotaDefinitionId(CloudFoundryClient cloudFoundryClient, String quotaDefinitionName) { return getOrganizationQuotaDefinition(cloudFoundryClient, quotaDefinitionName) .map(ResourceUtils::getId); } private static Mono<List<String>> getSpaceNames(CloudFoundryClient cloudFoundryClient, String organizationId) { return requestSpaces(cloudFoundryClient, organizationId) .map(resource -> ResourceUtils.getEntity(resource).getName()) .collectList(); } private static Mono<List<SpaceQuota>> getSpaceQuotas(CloudFoundryClient cloudFoundryClient, String organizationId) { return requestSpaceQuotaDefinitions(cloudFoundryClient, organizationId) .map(DefaultOrganizations::toSpaceQuota) .collectList(); } private static Mono<AssociateOrganizationManagerByUsernameResponse> requestAssociateOrganizationManagerByUsername(CloudFoundryClient cloudFoundryClient, String organizationId, String username) { return cloudFoundryClient.organizations() .associateManagerByUsername(AssociateOrganizationManagerByUsernameRequest.builder() .organizationId(organizationId) .username(username) .build()); } private static Mono<AssociateOrganizationUserByUsernameResponse> requestAssociateOrganizationUserByUsername(CloudFoundryClient cloudFoundryClient, String organizationId, String username) { return cloudFoundryClient.organizations() .associateUserByUsername(AssociateOrganizationUserByUsernameRequest.builder() .organizationId(organizationId) .username(username) .build()); } private static Mono<CreateOrganizationResponse> requestCreateOrganization(CloudFoundryClient cloudFoundryClient, String organization, String quotaDefinitionId) { return cloudFoundryClient.organizations() .create(org.cloudfoundry.client.v2.organizations.CreateOrganizationRequest .builder() .name(organization) .quotaDefinitionId(quotaDefinitionId) .build()); } private static Mono<DeleteOrganizationResponse> requestDeleteOrganization(CloudFoundryClient cloudFoundryClient, String organizationId) { return cloudFoundryClient.organizations() .delete(org.cloudfoundry.client.v2.organizations.DeleteOrganizationRequest.builder() .organizationId(organizationId) .async(true) .build()); } private static Mono<GetFeatureFlagResponse> requestGetFeatureFlag(CloudFoundryClient cloudFoundryClient, String featureFlag) { return cloudFoundryClient.featureFlags() .get(GetFeatureFlagRequest.builder() .name(featureFlag) .build()); } private static Flux<PrivateDomainResource> requestListPrivateDomains(CloudFoundryClient cloudFoundryClient, String organizationId) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.organizations() .listPrivateDomains(ListOrganizationPrivateDomainsRequest.builder() .organizationId(organizationId) .page(page) .build())); } private static Flux<SharedDomainResource> requestListSharedDomains(CloudFoundryClient cloudFoundryClient) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.sharedDomains() .list(ListSharedDomainsRequest.builder() .page(page) .build())); } private static Mono<GetOrganizationQuotaDefinitionResponse> requestOrganizationQuotaDefinition(CloudFoundryClient cloudFoundryClient, String quotaDefinitionId) { return cloudFoundryClient.organizationQuotaDefinitions() .get(GetOrganizationQuotaDefinitionRequest.builder() .organizationQuotaDefinitionId(quotaDefinitionId) .build()); } private static Flux<OrganizationQuotaDefinitionResource> requestOrganizationQuotaDefinitions(CloudFoundryClient cloudFoundryClient, String organizationQuotaDefinition) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.organizationQuotaDefinitions() .list(ListOrganizationQuotaDefinitionsRequest.builder() .name(organizationQuotaDefinition) .page(page) .build())); } private static Flux<OrganizationResource> requestOrganizations(CloudFoundryClient cloudFoundryClient, String organizationName) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.organizations() .list(ListOrganizationsRequest.builder() .name(organizationName) .page(page) .build())); } private static Flux<OrganizationResource> requestOrganizations(CloudFoundryClient cloudFoundryClient) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.organizations() .list(ListOrganizationsRequest.builder() .page(page) .build())); } private static Flux<SpaceQuotaDefinitionResource> requestSpaceQuotaDefinitions(CloudFoundryClient cloudFoundryClient, String organizationId) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.organizations() .listSpaceQuotaDefinitions(ListOrganizationSpaceQuotaDefinitionsRequest.builder() .page(page) .organizationId(organizationId) .build())); } private static Flux<SpaceResource> requestSpaces(CloudFoundryClient cloudFoundryClient, String organizationId) { return PaginationUtils .requestClientV2Resources(page -> cloudFoundryClient.organizations() .listSpaces(ListOrganizationSpacesRequest.builder() .page(page) .organizationId(organizationId) .build())); } private static Mono<UpdateOrganizationResponse> requestUpdateOrganization(CloudFoundryClient cloudFoundryClient, String organizationId, String newName) { return cloudFoundryClient.organizations() .update(UpdateOrganizationRequest.builder() .organizationId(organizationId) .name(newName) .build()); } private static Mono<Void> setOrganizationManager(CloudFoundryClient cloudFoundryClient, String organizationId, String username) { return Mono .when( requestAssociateOrganizationManagerByUsername(cloudFoundryClient, organizationId, username), requestAssociateOrganizationUserByUsername(cloudFoundryClient, organizationId, username) ) .then(); } private static OrganizationDetail toOrganizationDetail(List<String> domains, OrganizationQuota organizationQuota, List<SpaceQuota> spacesQuotas, List<String> spaces, OrganizationResource organizationResource, OrganizationInfoRequest organizationInfoRequest) { return OrganizationDetail.builder() .domains(domains) .id(ResourceUtils.getId(organizationResource)) .name(organizationInfoRequest.getName()) .quota(organizationQuota) .spaceQuotas(spacesQuotas) .spaces(spaces) .build(); } private static OrganizationQuota toOrganizationQuota(GetOrganizationQuotaDefinitionResponse response, OrganizationResource resource) { return OrganizationQuota.builder() .id(ResourceUtils.getId(response)) .organizationId(ResourceUtils.getId(resource)) .name(ResourceUtils.getEntity(response).getName()) .totalMemoryLimit(ResourceUtils.getEntity(response).getMemoryLimit()) .instanceMemoryLimit(ResourceUtils.getEntity(response).getInstanceMemoryLimit()) .totalRoutes(ResourceUtils.getEntity(response).getTotalRoutes()) .totalServiceInstances(ResourceUtils.getEntity(response).getTotalServices()) .paidServicePlans(ResourceUtils.getEntity(response).getNonBasicServicesAllowed()) .build(); } private static OrganizationSummary toOrganizationSummary(OrganizationResource resource) { return OrganizationSummary.builder() .id(ResourceUtils.getId(resource)) .name(ResourceUtils.getEntity(resource).getName()) .build(); } private static SpaceQuota toSpaceQuota(SpaceQuotaDefinitionResource resource) { return SpaceQuota.builder() .id(ResourceUtils.getId(resource)) .organizationId(ResourceUtils.getEntity(resource).getOrganizationId()) .name(ResourceUtils.getEntity(resource).getName()) .totalMemoryLimit(ResourceUtils.getEntity(resource).getMemoryLimit()) .instanceMemoryLimit(ResourceUtils.getEntity(resource).getInstanceMemoryLimit()) .totalRoutes(ResourceUtils.getEntity(resource).getTotalRoutes()) .totalServiceInstances(ResourceUtils.getEntity(resource).getTotalServices()) .paidServicePlans(ResourceUtils.getEntity(resource).getNonBasicServicesAllowed()) .build(); } }