From f5ed5cc612276f502292b1dd78d55da3f8559b7a Mon Sep 17 00:00:00 2001 From: theghost5800 Date: Thu, 9 Apr 2026 14:57:19 +0300 Subject: [PATCH 1/2] Handle multiple bindings between app and service JIRA:LMCROSSITXSADEPLOY-3409 --- .../ResilientCloudControllerClient.java | 13 ++- .../client/facade/CloudControllerClient.java | 18 ++--- .../facade/CloudControllerClientImpl.java | 15 ++-- .../rest/CloudControllerRestClient.java | 6 +- .../rest/CloudControllerRestClientImpl.java | 79 +++++++++++++------ .../client/LoggingCloudControllerClient.java | 12 +-- .../CheckServiceBindingOperationStep.java | 55 ++++++++----- ...eApplicationServiceBindingActionsStep.java | 2 +- ...lServiceBindingLastOperationExecution.java | 62 +++++++++------ ...erviceUnbindingLastOperationExecution.java | 8 +- ...ollServiceUnbindingOperationExecution.java | 23 ++++++ .../UnbindServiceFromApplicationStep.java | 24 ++++-- ...aultApplicationServicesUpdateCallback.java | 4 +- .../util/ServiceBindingParametersGetter.java | 26 ++++-- .../UnbindServiceFromApplicationCallback.java | 4 +- .../process/variables/Variables.java | 4 + .../BindServiceToApplicationStepTest.java | 2 +- .../CheckServiceBindingOperationStepTest.java | 37 +++++++-- ...licationServiceBindingActionsStepTest.java | 11 +++ ...viceBindingLastOperationExecutionTest.java | 31 ++++++-- ...erviceUnbindingOperationExecutionTest.java | 42 ++++++++++ .../UnbindServiceStepFromApplicationTest.java | 20 ++++- .../ServiceBindingParametersGetterTest.java | 13 ++- 23 files changed, 365 insertions(+), 146 deletions(-) diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java index 0cb70df31a..acffeeb34c 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/ResilientCloudControllerClient.java @@ -11,7 +11,6 @@ import java.util.function.Supplier; import org.cloudfoundry.client.v3.Metadata; -import org.cloudfoundry.multiapps.controller.client.util.ResilientCloudOperationExecutor; import org.cloudfoundry.multiapps.controller.client.facade.ApplicationServicesUpdateCallback; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClientImpl; @@ -282,8 +281,8 @@ public List getAppBindings(UUID applicationGuid) { } @Override - public CloudServiceBinding getServiceBindingForApplication(UUID applicationGuid, UUID serviceInstanceGuid) { - return executeWithRetry(() -> delegate.getServiceBindingForApplication(applicationGuid, serviceInstanceGuid)); + public List getServiceBindingsForApplication(UUID applicationGuid, UUID serviceInstanceGuid) { + return executeWithRetry(() -> delegate.getServiceBindingsForApplication(applicationGuid, serviceInstanceGuid)); } @Override @@ -332,12 +331,12 @@ public void stopApplication(String applicationName) { } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName) { return executeWithRetry(() -> delegate.unbindServiceInstance(applicationName, serviceInstanceName)); } @Override - public Optional unbindServiceInstance(UUID appGuid, UUID serviceGuid) { + public List unbindServiceInstance(UUID appGuid, UUID serviceGuid) { return executeWithRetry(() -> delegate.unbindServiceInstance(appGuid, serviceGuid)); } @@ -553,8 +552,8 @@ public List getBuildsForApplication(UUID applicationGuid) { } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName, - ApplicationServicesUpdateCallback applicationServicesUpdateCallback) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName, + ApplicationServicesUpdateCallback applicationServicesUpdateCallback) { return executeWithRetry( () -> delegate.unbindServiceInstance(applicationName, serviceInstanceName, applicationServicesUpdateCallback)); } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java index e8e057cbcc..2d8f04ccdf 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClient.java @@ -430,9 +430,9 @@ Optional bindServiceInstance(String bindingName, String applicationName, * * @param applicationId the GUID of the application * @param serviceInstanceGuid the GUID of the service instance - * @return the binding + * @return the bindings */ - CloudServiceBinding getServiceBindingForApplication(UUID applicationId, UUID serviceInstanceGuid); + List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid); /** * Get all service instance parameters. @@ -561,28 +561,28 @@ Optional bindServiceInstance(String bindingName, String applicationName, * @param applicationName the application name * @param serviceInstanceName the service instance name * @param applicationServicesUpdateCallback callback used for error handling - * @return job id for async polling if present + * @return List job ids for async polling if present */ - Optional unbindServiceInstance(String applicationName, String serviceInstanceName, - ApplicationServicesUpdateCallback applicationServicesUpdateCallback); + List unbindServiceInstance(String applicationName, String serviceInstanceName, + ApplicationServicesUpdateCallback applicationServicesUpdateCallback); /** * Un-associate (unprovision) a service from an application. * * @param applicationName the application name * @param serviceInstanceName the service instance name - * @return job id for async polling if present + * @return List of job ids for async polling if present */ - Optional unbindServiceInstance(String applicationName, String serviceInstanceName); + List unbindServiceInstance(String applicationName, String serviceInstanceName); /** * Un-associate (unprovision) a service from an application. * * @param applicationGuid the application guid * @param serviceInstanceGuid the service instance guid - * @return job id for async polling if present + * @return List of job ids for async polling if present */ - Optional unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid); + List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid); /** * Update application disk quota. diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java index 9bf88c3bc9..ca0386040c 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/CloudControllerClientImpl.java @@ -3,6 +3,7 @@ import java.net.URL; import java.nio.file.Path; import java.time.Duration; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -364,8 +365,8 @@ public List getAppBindings(UUID applicationGuid) { } @Override - public CloudServiceBinding getServiceBindingForApplication(UUID applicationId, UUID serviceInstanceGuid) { - return handleExceptions(() -> delegate.getServiceBindingForApplication(applicationId, serviceInstanceGuid)); + public List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid) { + return handleExceptions(() -> delegate.getServiceBindingsForApplication(applicationId, serviceInstanceGuid)); } @Override @@ -464,23 +465,23 @@ public void stopApplication(String applicationName) { } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName, - ApplicationServicesUpdateCallback applicationServicesUpdateCallback) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName, + ApplicationServicesUpdateCallback applicationServicesUpdateCallback) { try { return handleExceptions(() -> delegate.unbindServiceInstance(applicationName, serviceInstanceName)); } catch (CloudOperationException e) { applicationServicesUpdateCallback.onError(e, applicationName, serviceInstanceName); } - return Optional.empty(); + return Collections.emptyList(); } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName) { return handleExceptions(() -> delegate.unbindServiceInstance(applicationName, serviceInstanceName)); } @Override - public Optional unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { + public List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { return handleExceptions(() -> delegate.unbindServiceInstance(applicationGuid, serviceInstanceGuid)); } diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java index 61c3b2b8bd..8c79aec33e 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClient.java @@ -143,7 +143,7 @@ Optional bindServiceInstance(String bindingName, String applicationName, List getAppBindings(UUID applicationGuid); - CloudServiceBinding getServiceBindingForApplication(UUID applicationId, UUID serviceInstanceGuid); + List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid); CloudServiceBroker getServiceBroker(String name); @@ -179,9 +179,9 @@ Optional bindServiceInstance(String bindingName, String applicationName, void stopApplication(String applicationName); - Optional unbindServiceInstance(String applicationName, String serviceInstanceName); + List unbindServiceInstance(String applicationName, String serviceInstanceName); - Optional unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid); + List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid); void updateApplicationDiskQuota(String applicationName, int disk); diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java index d92af5d765..765718a49f 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java @@ -677,7 +677,8 @@ public Optional deleteServiceBinding(String serviceInstanceName, String if (serviceKey == null) { throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service key " + serviceKeyName + " not found."); } - return doDeleteServiceBinding(serviceKey.getGuid()); + return doDeleteServiceBindings(List.of(serviceKey.getGuid())).stream() + .findFirst(); } @Override @@ -912,9 +913,9 @@ public List getAppBindings(UUID applicationGuid) { } @Override - public CloudServiceBinding getServiceBindingForApplication(UUID applicationId, UUID serviceInstanceGuid) { - return fetch(() -> getServiceBindingResourceByApplicationGuidAndServiceInstanceGuid(applicationId, serviceInstanceGuid), - ImmutableRawCloudServiceBinding::of); + public List getServiceBindingsForApplication(UUID applicationId, UUID serviceInstanceGuid) { + return fetchList(() -> getServiceBindingResourcesByApplicationGuidAndServiceInstanceGuid(applicationId, serviceInstanceGuid), + ImmutableRawCloudServiceBinding::of); } @Override @@ -1257,7 +1258,7 @@ public void stopApplication(String applicationName) { } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName) { UUID applicationGuid = getRequiredApplicationGuid(applicationName); UUID serviceInstanceGuid = getRequiredServiceInstanceGuid(serviceInstanceName); @@ -1266,11 +1267,12 @@ public Optional unbindServiceInstance(String applicationName, String ser @Override public Optional deleteServiceBinding(UUID serviceBindingGuid) { - return doDeleteServiceBinding(serviceBindingGuid); + return doDeleteServiceBindings(List.of(serviceBindingGuid)).stream() + .findFirst(); } @Override - public Optional unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { + public List unbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { return doUnbindServiceInstance(applicationGuid, serviceInstanceGuid); } @@ -1736,8 +1738,8 @@ private Flux getServiceBindingResourcesByServi .list(pageRequestSupplier.apply(page))); } - private Mono getServiceBindingResourceByApplicationGuidAndServiceInstanceGuid(UUID applicationGuid, - UUID serviceInstanceGuid) { + private Flux getServiceBindingResourcesByApplicationGuidAndServiceInstanceGuid(UUID applicationGuid, + UUID serviceInstanceGuid) { IntFunction pageRequestSupplier = page -> ListServiceBindingsRequest.builder() .applicationId( applicationGuid.toString()) @@ -1745,7 +1747,7 @@ private Mono getServiceBindingResourceByApplic serviceInstanceGuid.toString()) .page(page) .build(); - return getApplicationServiceBindingResources(pageRequestSupplier).singleOrEmpty(); + return getApplicationServiceBindingResources(pageRequestSupplier); } private Flux getServiceBindingResourcesByApplicationGuid(UUID applicationGuid) { @@ -2035,23 +2037,51 @@ private void doDeleteServiceInstance(UUID serviceInstanceGuid) { .block(); } - private Optional doUnbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { - UUID serviceBindingGuid = getServiceBindingGuid(applicationGuid, serviceInstanceGuid); - if (serviceBindingGuid == null) { + private List doUnbindServiceInstance(UUID applicationGuid, UUID serviceInstanceGuid) { + List serviceBindingGuids = getServiceBindingGuids(applicationGuid, serviceInstanceGuid); + if (serviceBindingGuids.isEmpty()) { throw new CloudOperationException(HttpStatus.NOT_FOUND, "Not Found", "Service binding between service with GUID " + serviceInstanceGuid + " and application with GUID " + applicationGuid + " not found."); } - return doDeleteServiceBinding(serviceBindingGuid); + return doDeleteServiceBindings(serviceBindingGuids); } - private Optional doDeleteServiceBinding(UUID guid) { - String jobId = delegate.serviceBindingsV3() - .delete(DeleteServiceBindingRequest.builder() - .serviceBindingId(guid.toString()) - .build()) - .block(); - return Optional.ofNullable(jobId); + private List doDeleteServiceBindings(List guids) { + List jobIds = new ArrayList<>(); + List errors = new ArrayList<>(); + for (UUID guid : guids) { + handleServiceBindingDeletion(guid, jobIds, errors); + } + throwOnErrors(errors); + return jobIds; + } + + private void handleServiceBindingDeletion(UUID guid, List jobIds, List errors) { + try { + Optional.ofNullable(deleteSingleServiceBinding(guid)) + .ifPresent(jobIds::add); + } catch (RuntimeException e) { + errors.add(e); + } + } + + private String deleteSingleServiceBinding(UUID guid) { + return delegate.serviceBindingsV3() + .delete(DeleteServiceBindingRequest.builder() + .serviceBindingId(guid.toString()) + .build()) + .block(); + } + + private void throwOnErrors(List errors) { + if (errors.isEmpty()) { + return; + } + RuntimeException first = errors.get(0); + errors.subList(1, errors.size()) + .forEach(first::addSuppressed); + throw first; } private CloudDomain findDomainByName(String name, boolean required) { @@ -2463,9 +2493,10 @@ private UUID getRouteGuid(UUID domainGuid, String host, String path) { return getGuid(routeEntitiesResource.get(0)); } - private UUID getServiceBindingGuid(UUID applicationGuid, UUID serviceInstanceGuid) { - return getServiceBindingResourceByApplicationGuidAndServiceInstanceGuid(applicationGuid, serviceInstanceGuid).map(this::getGuid) - .block(); + private List getServiceBindingGuids(UUID applicationGuid, UUID serviceInstanceGuid) { + return getServiceBindingResourcesByApplicationGuidAndServiceInstanceGuid(applicationGuid, serviceInstanceGuid).map(this::getGuid) + .collectList() + .block(); } private void processAsyncUploadInBackground(CloudPackage cloudPackage, UploadStatusCallback callback) { diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java index a4376ac360..afb3fbdce1 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/client/LoggingCloudControllerClient.java @@ -396,9 +396,9 @@ public List getAppBindings(UUID applicationGuid) { } @Override - public CloudServiceBinding getServiceBindingForApplication(UUID applicationGuid, UUID serviceInstanceGuid) { + public List getServiceBindingsForApplication(UUID applicationGuid, UUID serviceInstanceGuid) { logger.debug(Messages.GETTING_BINDING_OF_SERVICE_INSTANCE_0_WITH_APPLICATION_1, serviceInstanceGuid, applicationGuid); - return delegate.getServiceBindingForApplication(applicationGuid, serviceInstanceGuid); + return delegate.getServiceBindingsForApplication(applicationGuid, serviceInstanceGuid); } @Override @@ -504,20 +504,20 @@ public void stopApplication(String applicationName) { } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName, - ApplicationServicesUpdateCallback applicationServicesUpdateCallback) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName, + ApplicationServicesUpdateCallback applicationServicesUpdateCallback) { logger.debug(Messages.UNBINDING_APPLICATION_0_FROM_SERVICE_INSTANCE_1, applicationName, serviceInstanceName); return delegate.unbindServiceInstance(applicationName, serviceInstanceName, applicationServicesUpdateCallback); } @Override - public Optional unbindServiceInstance(String applicationName, String serviceInstanceName) { + public List unbindServiceInstance(String applicationName, String serviceInstanceName) { logger.debug(Messages.UNBINDING_APPLICATION_0_FROM_SERVICE_INSTANCE_1, applicationName, serviceInstanceName); return delegate.unbindServiceInstance(applicationName, serviceInstanceName); } @Override - public Optional unbindServiceInstance(UUID appGuid, UUID serviceGuid) { + public List unbindServiceInstance(UUID appGuid, UUID serviceGuid) { logger.debug(Messages.UNBINDING_APPLICATION_0_FROM_SERVICE_INSTANCE_1, appGuid, serviceGuid); return delegate.unbindServiceInstance(appGuid, serviceGuid); } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java index cbbb9999a8..8269e1b7c6 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java @@ -1,6 +1,7 @@ package org.cloudfoundry.multiapps.controller.process.steps; import java.text.MessageFormat; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -22,61 +23,73 @@ public class CheckServiceBindingOperationStep extends AsyncFlowableStep { @Override protected StepPhase executeAsyncStep(ProcessContext context) throws Exception { - CloudServiceBinding serviceBinding = getServiceBindingForProcessing(context); - if (serviceBinding == null) { + List serviceBindings = getServiceBindingsForProcessing(context); + if (serviceBindings.isEmpty()) { return StepPhase.DONE; } - return checkServiceBindingOperationState(serviceBinding, context); + return checkServiceBindingsOperationState(serviceBindings, context); } - private CloudServiceBinding getServiceBindingForProcessing(ProcessContext context) { + private List getServiceBindingsForProcessing(ProcessContext context) { CloudServiceBinding serviceBindingToDelete = context.getVariable(Variables.SERVICE_BINDING_TO_DELETE); if (serviceBindingToDelete != null) { getStepLogger().debug(Messages.SERVICE_BINDING_0_SCHEDULED_FOR_DELETION, serviceBindingToDelete.getGuid()); - return serviceBindingToDelete; + return List.of(serviceBindingToDelete); } - return getServiceBindingForAppAndServiceInstance(context); + return getServiceBindingsForAppAndServiceInstance(context); } - private CloudServiceBinding getServiceBindingForAppAndServiceInstance(ProcessContext context) { + private List getServiceBindingsForAppAndServiceInstance(ProcessContext context) { CloudApplicationExtended app = context.getVariable(Variables.APP_TO_PROCESS); String serviceInstanceName = context.getVariable(Variables.SERVICE_TO_UNBIND_BIND); getStepLogger().debug(Messages.CHECKING_FOR_SERVICE_BINDING_OPERATION_IN_PROGRESS_BETWEEN_APP_0_AND_SERVICE_INSTANCE_1, app.getName(), serviceInstanceName); - return getCloudServiceBinding(context, app, serviceInstanceName); + return getCloudServiceBindings(context, app, serviceInstanceName); } - private CloudServiceBinding getCloudServiceBinding(ProcessContext context, CloudApplicationExtended app, String serviceInstanceName) { + private List getCloudServiceBindings(ProcessContext context, CloudApplicationExtended app, + String serviceInstanceName) { CloudControllerClient controllerClient = context.getControllerClient(); try { UUID applicationGuid = controllerClient.getApplicationGuid(app.getName()); UUID serviceInstanceGuid = controllerClient.getRequiredServiceInstanceGuid(serviceInstanceName); - return controllerClient.getServiceBindingForApplication(applicationGuid, serviceInstanceGuid); + return controllerClient.getServiceBindingsForApplication(applicationGuid, serviceInstanceGuid); } catch (CloudOperationException e) { List servicesToBind = context.getVariable(Variables.SERVICES_TO_BIND); if (StepsUtil.isServiceOptional(servicesToBind, serviceInstanceName)) { getStepLogger().warnWithoutProgressMessage(e, Messages.CANNOT_RETRIEVE_OPTIONAL_SERVICE_BINDING_FOR_SERVICE_INSTANCE_0, serviceInstanceName); - return null; + return Collections.emptyList(); } throw e; } } - private StepPhase checkServiceBindingOperationState(CloudServiceBinding serviceBinding, ProcessContext context) { - ServiceCredentialBindingOperation lastOperation = serviceBinding.getServiceBindingOperation(); - getStepLogger().debug(Messages.SERVICE_BINDING_OPERATION_WITH_TYPE_IS_IN_STATE, serviceBinding.getGuid(), lastOperation.getType(), - lastOperation.getState()); - if (lastOperation.getState() == ServiceCredentialBindingOperation.State.FAILED) { - getStepLogger().warnWithoutProgressMessage(Messages.SERVICE_BINDING_0_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED, - serviceBinding.getGuid()); + private StepPhase checkServiceBindingsOperationState(List serviceBindings, ProcessContext context) { + boolean hasFailedBindings = false; + boolean hasInProgressBindings = false; + for (CloudServiceBinding serviceBinding : serviceBindings) { + ServiceCredentialBindingOperation lastOperation = serviceBinding.getServiceBindingOperation(); + getStepLogger().debug(Messages.SERVICE_BINDING_OPERATION_WITH_TYPE_IS_IN_STATE, serviceBinding.getGuid(), + lastOperation.getType(), lastOperation.getState()); + if (lastOperation.getState() == ServiceCredentialBindingOperation.State.IN_PROGRESS + || lastOperation.getState() == ServiceCredentialBindingOperation.State.INITIAL) { + hasInProgressBindings = true; + } + if (lastOperation.getState() == ServiceCredentialBindingOperation.State.FAILED) { + getStepLogger().warnWithoutProgressMessage(Messages.SERVICE_BINDING_0_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED, + serviceBinding.getGuid()); + hasFailedBindings = true; + } + } + if (hasFailedBindings) { context.setVariable(Variables.SHOULD_RECREATE_SERVICE_BINDING, true); return StepPhase.DONE; } - if (lastOperation.getState() == ServiceCredentialBindingOperation.State.SUCCEEDED) { - return StepPhase.DONE; + if (hasInProgressBindings) { + return StepPhase.POLL; } - return StepPhase.POLL; + return StepPhase.DONE; } @Override diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStep.java index 89b8179557..2ff167cf0d 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStep.java @@ -134,7 +134,7 @@ private boolean shouldKeepExistingServiceBindings(CloudApplicationExtended app) } protected ServiceBindingParametersGetter getServiceBindingParametersGetter(ProcessContext context) { - return new ServiceBindingParametersGetter(context, archiveEntryExtractor, configuration.getMaxManifestSize(), fileService); + return new ServiceBindingParametersGetter(context, archiveEntryExtractor, configuration.getMaxManifestSize()); } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java index 1e5cfc7eb6..24887701c0 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java @@ -1,6 +1,7 @@ package org.cloudfoundry.multiapps.controller.process.steps; import java.text.MessageFormat; +import java.util.Collections; import java.util.List; import java.util.UUID; @@ -18,57 +19,70 @@ public class PollServiceBindingLastOperationExecution implements AsyncExecution @Override public AsyncExecutionState execute(ProcessContext context) { CloudControllerClient controllerClient = context.getControllerClient(); - CloudServiceBinding serviceBinding = getServiceBindingForProcessing(context, controllerClient); - if (serviceBinding == null) { + List serviceBindings = getServiceBindingsForProcessing(context, controllerClient); + if (serviceBindings.isEmpty()) { + context.getStepLogger() + .debug(Messages.SERVICE_BINDING_HAS_ALREADY_BEEN_DELETED); return AsyncExecutionState.FINISHED; } - return checkServiceBindingOperationState(serviceBinding, context); + return checkServiceBindingOperationState(serviceBindings, context); } - private CloudServiceBinding getServiceBindingForProcessing(ProcessContext context, CloudControllerClient controllerClient) { + private List getServiceBindingsForProcessing(ProcessContext context, CloudControllerClient controllerClient) { CloudServiceBinding serviceBindingToDelete = context.getVariable(Variables.SERVICE_BINDING_TO_DELETE); if (serviceBindingToDelete != null) { return getServiceBinding(context, controllerClient, serviceBindingToDelete); } - return getServiceBindingForAppAndServiceInstance(context, controllerClient); + return getServiceBindingsForAppAndServiceInstance(context, controllerClient); } - private CloudServiceBinding getServiceBinding(ProcessContext context, CloudControllerClient controllerClient, - CloudServiceBinding serviceBindingToDelete) { - CloudServiceBinding serviceBinding = controllerClient.getServiceBindingForApplication(serviceBindingToDelete.getApplicationGuid(), - serviceBindingToDelete.getServiceInstanceGuid()); + private List getServiceBinding(ProcessContext context, CloudControllerClient controllerClient, + CloudServiceBinding serviceBindingToDelete) { + CloudServiceBinding serviceBinding = controllerClient.getServiceBinding(serviceBindingToDelete.getGuid()); if (serviceBinding == null) { - context.getStepLogger() - .debug(Messages.SERVICE_BINDING_HAS_ALREADY_BEEN_DELETED); - return null; + return Collections.emptyList(); } context.getStepLogger() .debug(Messages.SERVICE_BINDING_0_SCHEDULED_FOR_DELETION_IS_IN_STATE_0, serviceBinding.getGuid(), serviceBinding.getServiceBindingOperation() .getState()); - return serviceBinding; + return List.of(serviceBinding); } - private CloudServiceBinding getServiceBindingForAppAndServiceInstance(ProcessContext context, CloudControllerClient controllerClient) { + private List getServiceBindingsForAppAndServiceInstance(ProcessContext context, + CloudControllerClient controllerClient) { CloudApplication app = context.getVariable(Variables.APP_TO_PROCESS); String serviceInstanceName = context.getVariable(Variables.SERVICE_TO_UNBIND_BIND); UUID applicationGuid = controllerClient.getApplicationGuid(app.getName()); UUID serviceInstanceGuid = controllerClient.getRequiredServiceInstanceGuid(serviceInstanceName); - return controllerClient.getServiceBindingForApplication(applicationGuid, serviceInstanceGuid); + return controllerClient.getServiceBindingsForApplication(applicationGuid, serviceInstanceGuid); } - protected AsyncExecutionState checkServiceBindingOperationState(CloudServiceBinding serviceBinding, ProcessContext context) { - ServiceCredentialBindingOperation lastOperation = serviceBinding.getServiceBindingOperation(); + protected AsyncExecutionState checkServiceBindingOperationState(List serviceBindings, ProcessContext context) { + CloudServiceBinding failedServiceBinding = null; + boolean hasInProgressBindings = false; StepLogger stepLogger = context.getStepLogger(); - stepLogger.debug(MessageFormat.format(Messages.SERVICE_BINDING_OPERATION_WITH_TYPE_IS_IN_STATE, serviceBinding.getGuid(), - lastOperation.getType(), lastOperation.getState())); - if (lastOperation.getState() == ServiceCredentialBindingOperation.State.FAILED) { - return completePollingOfFailedOperation(serviceBinding, context); + + for (CloudServiceBinding serviceBinding : serviceBindings) { + ServiceCredentialBindingOperation lastOperation = serviceBinding.getServiceBindingOperation(); + stepLogger.debug(MessageFormat.format(Messages.SERVICE_BINDING_OPERATION_WITH_TYPE_IS_IN_STATE, serviceBinding.getGuid(), + lastOperation.getType(), lastOperation.getState())); + if (lastOperation.getState() == ServiceCredentialBindingOperation.State.FAILED) { + failedServiceBinding = serviceBinding; + } + if (lastOperation.getState() == ServiceCredentialBindingOperation.State.IN_PROGRESS + || lastOperation.getState() == ServiceCredentialBindingOperation.State.INITIAL) { + hasInProgressBindings = true; + } } - if (lastOperation.getState() == ServiceCredentialBindingOperation.State.SUCCEEDED) { - return AsyncExecutionState.FINISHED; + + if (failedServiceBinding != null) { + return completePollingOfFailedOperation(failedServiceBinding, context); + } + if (hasInProgressBindings) { + return AsyncExecutionState.RUNNING; } - return AsyncExecutionState.RUNNING; + return AsyncExecutionState.FINISHED; } protected AsyncExecutionState completePollingOfFailedOperation(CloudServiceBinding serviceBinding, ProcessContext context) { diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java index bbc7debd4c..b8673f1969 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java @@ -1,15 +1,17 @@ package org.cloudfoundry.multiapps.controller.process.steps; +import java.util.List; + import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; public class PollServiceUnbindingLastOperationExecution extends PollServiceBindingLastOperationExecution { @Override - protected AsyncExecutionState checkServiceBindingOperationState(CloudServiceBinding serviceBinding, ProcessContext context) { - if (serviceBinding == null) { + protected AsyncExecutionState checkServiceBindingOperationState(List serviceBindings, ProcessContext context) { + if (serviceBindings.isEmpty()) { return AsyncExecutionState.FINISHED; } - return super.checkServiceBindingOperationState(serviceBinding, context); + return super.checkServiceBindingOperationState(serviceBindings, context); } } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java index b311f469c2..c94205a0dc 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java @@ -1,9 +1,32 @@ package org.cloudfoundry.multiapps.controller.process.steps; +import java.util.ArrayList; +import java.util.List; + import org.cloudfoundry.multiapps.controller.process.variables.Variables; public class PollServiceUnbindingOperationExecution extends PollServiceBindingUnbindingOperationBaseExecution { + @Override + public AsyncExecutionState execute(ProcessContext context) { + List jobIds = context.getVariable(Variables.SERVICE_UNBINDING_JOB_IDS); + if (jobIds.isEmpty()) { + return super.execute(context); + } + List remainingJobIds = new ArrayList<>(jobIds); + for (String jobId : jobIds) { + context.setVariable(Variables.SERVICE_UNBINDING_JOB_ID, jobId); + AsyncExecutionState state = super.execute(context); + if (state == AsyncExecutionState.FINISHED) { + remainingJobIds.remove(jobId); + context.setVariable(Variables.SERVICE_UNBINDING_JOB_IDS, remainingJobIds); + continue; + } + return state; + } + return AsyncExecutionState.FINISHED; + } + @Override protected String getAsyncJobId(ProcessContext context) { return context.getVariable(Variables.SERVICE_UNBINDING_JOB_ID); diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceFromApplicationStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceFromApplicationStep.java index 4d51c2ab64..ababdea3c3 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceFromApplicationStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceFromApplicationStep.java @@ -38,11 +38,11 @@ protected StepPhase executeAsyncStep(ProcessContext context) throws Exception { CloudApplicationExtended app = context.getVariable(Variables.APP_TO_PROCESS); String serviceInstanceName = context.getVariable(Variables.SERVICE_TO_UNBIND_BIND); getStepLogger().info(Messages.UNBINDING_SERVICE_INSTANCE_FROM_APP, serviceInstanceName, app.getName()); - return deleteServiceBinding(context, () -> controllerClient.unbindServiceInstance(app.getName(), serviceInstanceName, - getApplicationServicesUpdateCallback(context, - controllerClient)), - () -> MessageFormat.format(Messages.UNBINDING_SERVICE_INSTANCE_FROM_APP_FINISHED, serviceInstanceName, - app.getName())); + return deleteServiceBindings(context, () -> controllerClient.unbindServiceInstance(app.getName(), serviceInstanceName, + getApplicationServicesUpdateCallback(context, + controllerClient)), + () -> MessageFormat.format(Messages.UNBINDING_SERVICE_INSTANCE_FROM_APP_FINISHED, serviceInstanceName, + app.getName())); } private StepPhase deleteServiceBinding(ProcessContext context, Supplier> serviceBindingJobSupplier, @@ -59,6 +59,20 @@ private StepPhase deleteServiceBinding(ProcessContext context, Supplier> serviceBindingJobsSupplier, + Supplier messageSupplier) { + List jobIds = serviceBindingJobsSupplier.get(); + if (context.getVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_DELETION)) { + return StepPhase.POLL; + } + if (jobIds.isEmpty()) { + getStepLogger().infoWithoutProgressMessage(messageSupplier.get()); + return StepPhase.DONE; + } + context.setVariable(Variables.SERVICE_UNBINDING_JOB_IDS, jobIds); + return StepPhase.POLL; + } + @Override protected List getAsyncStepExecutions(ProcessContext context) { AsyncExecution pollingExecution = createServiceBindingPollingFactory(context).createPollingExecution(); diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DefaultApplicationServicesUpdateCallback.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DefaultApplicationServicesUpdateCallback.java index 3f55b20c2e..4aa1e68366 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DefaultApplicationServicesUpdateCallback.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/DefaultApplicationServicesUpdateCallback.java @@ -29,8 +29,8 @@ public void onError(CloudOperationException e, String applicationName, String se if (e.getStatusCode() == HttpStatus.UNPROCESSABLE_ENTITY) { UUID applicationGuid = controllerClient.getApplicationGuid(applicationName); UUID serviceInstanceGuid = controllerClient.getRequiredServiceInstanceGuid(serviceInstanceName); - CloudServiceBinding serviceBinding = controllerClient.getServiceBindingForApplication(applicationGuid, serviceInstanceGuid); - if (serviceBinding != null) { + List serviceBindings = controllerClient.getServiceBindingsForApplication(applicationGuid, serviceInstanceGuid); + if (!serviceBindings.isEmpty()) { context.getStepLogger() .warnWithoutProgressMessage(e, Messages.SERVICE_BINDING_BETWEEN_SERVICE_0_AND_APP_1_ALREADY_CREATED, serviceInstanceName, applicationName); diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java index 36d6f7a5a5..c44fcf6771 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java @@ -3,6 +3,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -14,12 +15,12 @@ import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudApplication; import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; import org.cloudfoundry.multiapps.controller.client.lib.domain.BindingDetails; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.core.helpers.MtaArchiveElements; import org.cloudfoundry.multiapps.controller.core.security.serialization.DynamicSecureSerialization; -import org.cloudfoundry.multiapps.controller.persistence.services.FileService; import org.cloudfoundry.multiapps.controller.process.Messages; import org.cloudfoundry.multiapps.controller.process.security.util.SecureLoggingUtil; import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext; @@ -34,14 +35,11 @@ public class ServiceBindingParametersGetter { private final ArchiveEntryExtractor archiveEntryExtractor; private final long maxManifestSize; - private final FileService fileService; - public ServiceBindingParametersGetter(ProcessContext context, ArchiveEntryExtractor archiveEntryExtractor, long maxManifestSize, - FileService fileService) { + public ServiceBindingParametersGetter(ProcessContext context, ArchiveEntryExtractor archiveEntryExtractor, long maxManifestSize) { this.context = context; this.archiveEntryExtractor = archiveEntryExtractor; this.maxManifestSize = maxManifestSize; - this.fileService = fileService; } public Map getServiceBindingParametersFromMta(CloudApplicationExtended app, String serviceName) { @@ -131,7 +129,14 @@ public String getDescriptorProvidedBindingName(CloudApplicationExtended app, Str public Map getServiceBindingParametersFromExistingInstance(CloudApplication application, String serviceName) { CloudControllerClient client = context.getControllerClient(); UUID serviceGuid = client.getRequiredServiceInstanceGuid(serviceName); - CloudServiceBinding serviceBinding = client.getServiceBindingForApplication(application.getGuid(), serviceGuid); + return getServiceBindingParametersFromExistingInstance(application, serviceName, serviceGuid); + } + + public Map getServiceBindingParametersFromExistingInstance(CloudApplication application, String serviceName, + UUID serviceInstanceGuid) { + CloudControllerClient client = context.getControllerClient(); + List serviceBindings = client.getServiceBindingsForApplication(application.getGuid(), serviceInstanceGuid); + CloudServiceBinding serviceBinding = selectNewestBinding(serviceBindings); if (serviceBinding == null) { throw new SLException(Messages.SERVICE_INSTANCE_0_NOT_BOUND_TO_APP_1, serviceName, application.getName()); } @@ -158,4 +163,13 @@ public Map getServiceBindingParametersFromExistingInstance(Cloud } } + private CloudServiceBinding selectNewestBinding(List bindings) { + return bindings.stream() + .filter(binding -> binding.getServiceBindingOperation() + .getState() == ServiceCredentialBindingOperation.State.SUCCEEDED) + .max(Comparator.comparing(binding -> binding.getMetadata() + .getCreatedAt())) + .orElseGet(() -> bindings.isEmpty() ? null : bindings.get(0)); + } + } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/UnbindServiceFromApplicationCallback.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/UnbindServiceFromApplicationCallback.java index 1a3ac535ac..00a475b0c2 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/UnbindServiceFromApplicationCallback.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/UnbindServiceFromApplicationCallback.java @@ -36,8 +36,8 @@ public void onError(CloudOperationException e, String applicationName, String se if (e.getStatusCode() == HttpStatus.UNPROCESSABLE_ENTITY) { UUID applicationGuid = controllerClient.getApplicationGuid(applicationName); UUID serviceInstanceGuid = controllerClient.getRequiredServiceInstanceGuid(serviceInstanceName); - CloudServiceBinding serviceBinding = controllerClient.getServiceBindingForApplication(applicationGuid, serviceInstanceGuid); - if (serviceBinding != null) { + List serviceBindings = controllerClient.getServiceBindingsForApplication(applicationGuid, serviceInstanceGuid); + if (!serviceBindings.isEmpty()) { context.setVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_DELETION, true); return; } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/variables/Variables.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/variables/Variables.java index 8c35932c48..23cabdea7f 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/variables/Variables.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/variables/Variables.java @@ -756,6 +756,10 @@ public Serializer> getSerializer() { Variable SERVICE_UNBINDING_JOB_ID = ImmutableSimpleVariable. builder() .name("serviceUnbindingJobId") .build(); + Variable> SERVICE_UNBINDING_JOB_IDS = ImmutableSimpleVariable.> builder() + .name("serviceUnbindingJobIds") + .defaultValue(Collections.emptyList()) + .build(); Variable SERVICE_KEY_CREATION_JOB_ID = ImmutableSimpleVariable. builder() .name("serviceKeyCreationJobId") .build(); diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BindServiceToApplicationStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BindServiceToApplicationStepTest.java index c777fa0089..2c945a87e7 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BindServiceToApplicationStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/BindServiceToApplicationStepTest.java @@ -122,7 +122,7 @@ private void handleErrorInCallback(ProcessContext customProcessContext) { private void handleServiceAlreadyBoundErrorInCallback(ProcessContext customProcessContext) { CloudOperationException cloudException = new CloudControllerException(HttpStatus.UNPROCESSABLE_ENTITY); - when(client.getServiceBindingForApplication(any(), any())).thenReturn(buildCloudServiceBinding()); + when(client.getServiceBindingsForApplication(any(), any())).thenReturn(List.of(buildCloudServiceBinding())); new DefaultApplicationServicesUpdateCallback(customProcessContext, client).onError(cloudException, APPLICATION_NAME, SERVICE_NAME); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStepTest.java index b2f6558882..2488f18acb 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStepTest.java @@ -44,6 +44,9 @@ void setUp() { @Test void testCheckBindingWhenBindingDoesNotExist() { + when(client.getServiceBindingsForApplication(APP_GUID, SERVICE_INSTANCE_GUID)).thenReturn(List.of()); + when(client.getApplicationGuid(APP_NAME)).thenReturn(APP_GUID); + when(client.getRequiredServiceInstanceGuid(SERVICE_INSTANCE_NAME)).thenReturn(SERVICE_INSTANCE_GUID); step.execute(execution); assertStepFinishedSuccessfully(); } @@ -68,17 +71,41 @@ static Stream testCheckServiceBindingWhichIsInDifferentStates() { void testCheckServiceBindingWhichIsInDifferentStates(ServiceCredentialBindingOperation.State bindingState, StepPhase expectedStepPhase) { CloudServiceBinding serviceBinding = buildCloudServiceBinding(bindingState); - when(client.getServiceBindingForApplication(APP_GUID, SERVICE_INSTANCE_GUID)).thenReturn(serviceBinding); + when(client.getServiceBindingsForApplication(APP_GUID, SERVICE_INSTANCE_GUID)).thenReturn(List.of(serviceBinding)); when(client.getApplicationGuid(APP_NAME)).thenReturn(APP_GUID); when(client.getRequiredServiceInstanceGuid(SERVICE_INSTANCE_NAME)).thenReturn(SERVICE_INSTANCE_GUID); step.execute(execution); assertEquals(expectedStepPhase.toString(), getExecutionStatus()); } + @Test + void testCheckServiceBindingWhichIsInDifferentStatesMultipleBindings() { + CloudServiceBinding succeededBinding = buildCloudServiceBinding(ServiceCredentialBindingOperation.State.SUCCEEDED); + CloudServiceBinding inProgressBinding = buildCloudServiceBinding(ServiceCredentialBindingOperation.State.IN_PROGRESS); + // when any binding is non-SUCCEEDED, the non-SUCCEEDED one is selected for state checking + when(client.getServiceBindingsForApplication(APP_GUID, SERVICE_INSTANCE_GUID)).thenReturn( + List.of(succeededBinding, inProgressBinding)); + when(client.getApplicationGuid(APP_NAME)).thenReturn(APP_GUID); + when(client.getRequiredServiceInstanceGuid(SERVICE_INSTANCE_NAME)).thenReturn(SERVICE_INSTANCE_GUID); + step.execute(execution); + assertEquals(StepPhase.POLL.toString(), getExecutionStatus()); + } + + @Test + void testCheckServiceBindingAllSucceededMultipleBindings() { + CloudServiceBinding binding1 = buildCloudServiceBinding(ServiceCredentialBindingOperation.State.SUCCEEDED); + CloudServiceBinding binding2 = buildCloudServiceBinding(ServiceCredentialBindingOperation.State.SUCCEEDED); + when(client.getServiceBindingsForApplication(APP_GUID, SERVICE_INSTANCE_GUID)).thenReturn(List.of(binding1, binding2)); + when(client.getApplicationGuid(APP_NAME)).thenReturn(APP_GUID); + when(client.getRequiredServiceInstanceGuid(SERVICE_INSTANCE_NAME)).thenReturn(SERVICE_INSTANCE_GUID); + step.execute(execution); + assertStepFinishedSuccessfully(); + } + @Test void testThrowExceptionWhenFetchingServiceBinding() { - when(client.getServiceBindingForApplication(APP_GUID, - SERVICE_INSTANCE_GUID)).thenThrow( + when(client.getServiceBindingsForApplication(APP_GUID, + SERVICE_INSTANCE_GUID)).thenThrow( new CloudOperationException(HttpStatus.SERVICE_UNAVAILABLE)); when(client.getApplicationGuid(APP_NAME)).thenReturn(APP_GUID); when(client.getRequiredServiceInstanceGuid(SERVICE_INSTANCE_NAME)).thenReturn(SERVICE_INSTANCE_GUID); @@ -91,8 +118,8 @@ void testThrowExceptionWhenFetchingServiceBinding() { @Test void testThrowExceptionWhenFetchingServiceBindingWithOptionalService() { - when(client.getServiceBindingForApplication(APP_GUID, - SERVICE_INSTANCE_GUID)).thenThrow(new CloudOperationException(HttpStatus.BAD_GATEWAY)); + when(client.getServiceBindingsForApplication(APP_GUID, + SERVICE_INSTANCE_GUID)).thenThrow(new CloudOperationException(HttpStatus.BAD_GATEWAY)); CloudServiceInstanceExtended optionalServiceInstance = buildOptionalCloudServiceInstanceExtended(); context.setVariable(Variables.SERVICES_TO_BIND, List.of(optionalServiceInstance)); when(client.getApplicationGuid(APP_NAME)).thenReturn(APP_GUID); diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStepTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStepTest.java index 908a33858f..566dff13a0 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStepTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/DetermineApplicationServiceBindingActionsStepTest.java @@ -35,6 +35,7 @@ class DetermineApplicationServiceBindingActionsStepTest extends SyncFlowableStep private static final String APP_NAME = "test_application"; private static final String SERVICE_INSTANCE_NAME = "test_service"; + private static final UUID SERVICE_INSTANCE_GUID = UUID.randomUUID(); @Mock private ServiceBindingParametersGetter serviceBindingParametersGetter; @@ -140,6 +141,13 @@ private ImmutableCloudServiceBinding buildServiceBinding() { .build(); } + private CloudServiceBinding buildExistingBinding(UUID appGuid) { + return ImmutableCloudServiceBinding.builder() + .applicationGuid(appGuid) + .serviceInstanceGuid(SERVICE_INSTANCE_GUID) + .build(); + } + private CloudApplicationExtended buildCloudApplicationExtended(boolean servicePartFromMta, boolean keepExistingBinding) { CloudApplicationExtended.AttributeUpdateStrategy attributeUpdateStrategy = ImmutableCloudApplicationExtended.ImmutableAttributeUpdateStrategy.builder() .shouldKeepExistingServiceBindings( @@ -162,6 +170,7 @@ private void prepareContext(CloudApplicationExtended application) { private void prepareClient(CloudApplicationExtended application, boolean serviceBindingExist) { when(client.getApplication(APP_NAME)).thenReturn(application); + when(client.getRequiredServiceInstanceGuid(SERVICE_INSTANCE_NAME)).thenReturn(SERVICE_INSTANCE_GUID); if (serviceBindingExist) { when(appServicesGetter.getServiceInstanceNamesBoundToApp(application.getGuid())).thenReturn(List.of(SERVICE_INSTANCE_NAME)); } @@ -172,6 +181,8 @@ private void prepareServiceBindingParametersGetter(Map mtaBindin when(serviceBindingParametersGetter.getServiceBindingParametersFromMta(any(), any())).thenReturn(mtaBindingParameters); when(serviceBindingParametersGetter.getServiceBindingParametersFromExistingInstance(any(), any())).thenReturn( existingServiceBindingParameters); + when(serviceBindingParametersGetter.getServiceBindingParametersFromExistingInstance(any(), any(), any())).thenReturn( + existingServiceBindingParameters); } @Override diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java index aa2646214b..31277416e4 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java @@ -50,7 +50,7 @@ static Stream testPollExecution() { @MethodSource void testPollExecution(CloudServiceBinding serviceBinding, AsyncExecutionState asyncExecutionState) { expectedExecutionStatus = asyncExecutionState; - initializeParameters(serviceBinding); + initializeParameters(List.of(serviceBinding)); testExecuteOperations(); } @@ -58,7 +58,7 @@ void testPollExecution(CloudServiceBinding serviceBinding, AsyncExecutionState a void testFailedExecutionForOptionalService() { expectedExecutionStatus = AsyncExecutionState.FINISHED; context.setVariable(Variables.SERVICES_TO_BIND, List.of(buildCloudServiceInstance(SERVICE_TO_UNBIND_BIND, true))); - initializeParameters(buildCloudServiceBinding("failed")); + initializeParameters(List.of(buildCloudServiceBinding("failed"))); testExecuteOperations(); } @@ -67,8 +67,8 @@ void testBindingBeingDeletedButIsInProgress() { expectedExecutionStatus = AsyncExecutionState.RUNNING; CloudServiceBinding serviceBinding = buildCloudServiceBinding("in progress"); context.setVariable(Variables.SERVICE_BINDING_TO_DELETE, serviceBinding); - when(client.getServiceBindingForApplication(any(UUID.class), any(UUID.class))).thenReturn(serviceBinding); - initializeParameters(serviceBinding); + when(client.getServiceBinding(any(UUID.class))).thenReturn(serviceBinding); + initializeParameters(List.of(serviceBinding)); testExecuteOperations(); } @@ -77,8 +77,8 @@ void testBindingToDeleteIsAlreadyDeleted() { expectedExecutionStatus = AsyncExecutionState.FINISHED; CloudServiceBinding serviceBinding = buildCloudServiceBinding("in progress"); context.setVariable(Variables.SERVICE_BINDING_TO_DELETE, serviceBinding); - initializeParameters(serviceBinding); - when(client.getServiceBindingForApplication(any(), any())).thenReturn(null); + initializeParameters(List.of(serviceBinding)); + when(client.getServiceBinding(any(UUID.class))).thenReturn(null); testExecuteOperations(); } @@ -93,6 +93,21 @@ void testPollingErrorMessage() { asyncExecutions.forEach(asyncExecution -> assertEquals(expectedErrorMessage, asyncExecution.getPollingErrorMessage(context))); } + @Test + void testBindingToDeleteSelectedByGuidWhenMultipleExist() { + expectedExecutionStatus = AsyncExecutionState.RUNNING; + CloudServiceBinding targetBinding = buildCloudServiceBinding("in progress"); + CloudServiceBinding otherBinding = ImmutableCloudServiceBinding.builder() + .name("other-binding") + .applicationGuid(SERVICE_BINDING_APP_GUID) + .serviceInstanceGuid(SERVICE_BINDING_SERVICE_INSTANCE_GUID) + .serviceBindingOperation(buildServiceBindingOperation("succeeded")) + .metadata(buildCloudMetadata(UUID.randomUUID())) + .build(); + initializeParameters(List.of(otherBinding, targetBinding)); + testExecuteOperations(); + } + private static CloudServiceBinding buildCloudServiceBinding(String lastOperationState) { return ImmutableCloudServiceBinding.builder() .name("test-service-binding") @@ -128,12 +143,12 @@ private static CloudMetadata buildCloudMetadata(UUID guid) { .build(); } - private void initializeParameters(CloudServiceBinding serviceBinding) { + private void initializeParameters(List serviceBindings) { context.setVariable(Variables.APP_TO_PROCESS, buildCloudApplication()); context.setVariable(Variables.SERVICE_TO_UNBIND_BIND, SERVICE_TO_UNBIND_BIND); when(client.getApplicationGuid(APP_TO_PROCESS_NAME)).thenReturn(APP_TO_PROCESS_GUID); when(client.getRequiredServiceInstanceGuid(SERVICE_TO_UNBIND_BIND)).thenReturn(SERVICE_TO_UNBIND_BIND_GUID); - when(client.getServiceBindingForApplication(any(), any())).thenReturn(serviceBinding); + when(client.getServiceBindingsForApplication(any(), any())).thenReturn(serviceBindings); } private CloudApplicationExtended buildCloudApplication() { diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java index 72621bd1b6..5008cb1269 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java @@ -181,6 +181,48 @@ void testGetOnErrorHandlerWithServiceToDelete() { assertTrue(errorMessage.contains(SERVICE_NAME)); } + @Test + void testMultipleJobIdsFirstFinishedSecondInProgress() { + String secondJobId = UUID.randomUUID().toString(); + context.setVariable(Variables.SERVICE_UNBINDING_JOB_IDS, List.of(JOB_ID, secondJobId)); + context.setVariable(Variables.APP_TO_PROCESS, ImmutableCloudApplicationExtended.builder() + .name(APP_NAME) + .metadata(ImmutableCloudMetadata.builder() + .guid(UUID.randomUUID()) + .build()) + .build()); + context.setVariable(Variables.SERVICE_TO_UNBIND_BIND, SERVICE_NAME); + context.setVariable(Variables.SERVICES_TO_BIND, List.of()); + when(client.getAsyncJob(JOB_ID)).thenReturn(buildAsyncJobWithState(JobState.COMPLETE)); + when(client.getAsyncJob(secondJobId)).thenReturn(buildAsyncJobWithState(JobState.PROCESSING)); + expectedAsyncExecutionState = AsyncExecutionState.RUNNING; + + testExecuteOperations(); + + assertEquals(List.of(secondJobId), context.getVariable(Variables.SERVICE_UNBINDING_JOB_IDS)); + } + + @Test + void testMultipleJobIdsAllFinished() { + String secondJobId = UUID.randomUUID().toString(); + context.setVariable(Variables.SERVICE_UNBINDING_JOB_IDS, List.of(JOB_ID, secondJobId)); + context.setVariable(Variables.APP_TO_PROCESS, ImmutableCloudApplicationExtended.builder() + .name(APP_NAME) + .metadata(ImmutableCloudMetadata.builder() + .guid(UUID.randomUUID()) + .build()) + .build()); + context.setVariable(Variables.SERVICE_TO_UNBIND_BIND, SERVICE_NAME); + context.setVariable(Variables.SERVICES_TO_BIND, List.of()); + when(client.getAsyncJob(JOB_ID)).thenReturn(buildAsyncJobWithState(JobState.COMPLETE)); + when(client.getAsyncJob(secondJobId)).thenReturn(buildAsyncJobWithState(JobState.COMPLETE)); + expectedAsyncExecutionState = AsyncExecutionState.FINISHED; + + testExecuteOperations(); + + assertTrue(context.getVariable(Variables.SERVICE_UNBINDING_JOB_IDS).isEmpty()); + } + private void prepareContext() { context.setVariable(Variables.SERVICE_UNBINDING_JOB_ID, JOB_ID); context.setVariable(Variables.APP_TO_PROCESS, ImmutableCloudApplicationExtended.builder() diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceStepFromApplicationTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceStepFromApplicationTest.java index 16a249cfa1..6233aae968 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceStepFromApplicationTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/UnbindServiceStepFromApplicationTest.java @@ -3,7 +3,6 @@ import java.text.MessageFormat; import java.time.LocalDateTime; import java.util.List; -import java.util.Optional; import java.util.UUID; import org.cloudfoundry.multiapps.common.SLException; @@ -46,7 +45,7 @@ class UnbindServiceStepFromApplicationTest extends SyncFlowableStepTest { context.setVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_DELETION, true); - return Optional.empty(); + return List.of(); }); step.execute(execution); assertEquals(StepPhase.POLL.toString(), getExecutionStatus()); @@ -74,6 +75,17 @@ void testAsyncServiceUnbindingWithPollingByLastOperation() { assertTrue(context.getVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_DELETION)); } + @Test + void testAsyncUnbindServiceWithMultipleBindings() { + prepareContext(); + String secondJobId = "456"; + when(client.unbindServiceInstance(eq(APPLICATION_NAME), eq(SERVICE_NAME), + any(ApplicationServicesUpdateCallback.class))).thenReturn(List.of(JOB_ID, secondJobId)); + step.execute(execution); + assertEquals(StepPhase.POLL.toString(), getExecutionStatus()); + assertEquals(List.of(JOB_ID, secondJobId), context.getVariable(Variables.SERVICE_UNBINDING_JOB_IDS)); + } + @Test void testDeleteServiceBinding() { context.setVariable(Variables.SERVICE_BINDING_TO_DELETE, buildCloudServiceBinding()); @@ -132,7 +144,7 @@ void testDoNotThrowExceptionWhenServiceBindingAlreadyDeletedByOperationCallback( void testDoNotThrowExceptionWhenDeletionIsInProgressByApplicationCallback() { ProcessContext customProcessContext = new ProcessContext(execution, stepLogger, clientProvider); CloudServiceBinding serviceBinding = buildCloudServiceBinding(); - when(client.getServiceBindingForApplication(any(), any())).thenReturn(serviceBinding); + when(client.getServiceBindingsForApplication(any(), any())).thenReturn(List.of(serviceBinding)); assertDoesNotThrow(() -> handleErrorInApplicationCallback(customProcessContext, HttpStatus.UNPROCESSABLE_ENTITY)); assertTrue(customProcessContext.getVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_DELETION)); } diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetterTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetterTest.java index bdffb03488..283708b358 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetterTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetterTest.java @@ -6,6 +6,7 @@ import java.util.UUID; import java.util.stream.Stream; +import org.cloudfoundry.multiapps.common.SLException; import org.cloudfoundry.multiapps.common.util.JsonUtil; import org.cloudfoundry.multiapps.controller.client.facade.CloudControllerClient; import org.cloudfoundry.multiapps.controller.client.facade.CloudOperationException; @@ -21,7 +22,6 @@ import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudApplicationExtended; import org.cloudfoundry.multiapps.controller.client.lib.domain.ImmutableCloudServiceInstanceExtended; import org.cloudfoundry.multiapps.controller.core.helpers.MtaArchiveElements; -import org.cloudfoundry.multiapps.controller.persistence.services.FileService; import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext; import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.junit.jupiter.api.BeforeEach; @@ -74,15 +74,12 @@ class ServiceBindingParametersGetterTest { @Mock private CloudControllerClient client; - @Mock - private FileService fileService; - private ServiceBindingParametersGetter serviceBindingParametersGetter; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); - serviceBindingParametersGetter = new ServiceBindingParametersGetter(context, archiveEntryExtractor, 0, fileService); + serviceBindingParametersGetter = new ServiceBindingParametersGetter(context, archiveEntryExtractor, 0); } @@ -145,7 +142,7 @@ void testThrowingExceptionWhenServiceBindingIsMissing() { prepareContext(serviceInstance); prepareClient(null, false); - assertThrows(CloudOperationException.class, + assertThrows(SLException.class, () -> serviceBindingParametersGetter.getServiceBindingParametersFromExistingInstance(application, SERVICE_NAME)); } @@ -245,10 +242,10 @@ private void prepareClient(Map bindingParameters, boolean servic ServiceCredentialBindingOperation.State.SUCCEEDED) .build()) .build(); - when(client.getServiceBindingForApplication(RANDOM_GUID, RANDOM_GUID)).thenReturn(serviceBinding); + when(client.getServiceBindingsForApplication(RANDOM_GUID, RANDOM_GUID)).thenReturn(List.of(serviceBinding)); return; } - when(client.getServiceBindingForApplication(RANDOM_GUID, RANDOM_GUID)).thenThrow(new CloudOperationException(HttpStatus.NOT_FOUND)); + when(client.getServiceBindingsForApplication(RANDOM_GUID, RANDOM_GUID)).thenReturn(List.of()); } } From 7a54f1e1c030e962bd21d2df3d735c006eba9593 Mon Sep 17 00:00:00 2001 From: theghost5800 Date: Tue, 28 Apr 2026 16:59:19 +0300 Subject: [PATCH 2/2] Fix comments --- .../rest/CloudControllerRestClientImpl.java | 20 +++++------ .../controller/process/Messages.java | 5 +++ .../CheckServiceBindingOperationStep.java | 2 +- ...BindingLastOperationFailSafeExecution.java | 19 ---------- ...erviceBindingsLastOperationExecution.java} | 35 ++++++++++++++----- ...indingsLastOperationFailSafeExecution.java | 29 +++++++++++++++ ...viceUnbindingsLastOperationExecution.java} | 2 +- ...lServiceUnbindingsOperationExecution.java} | 4 +-- .../util/ServiceBindingParametersGetter.java | 13 ++++--- .../util/ServiceBindingPollingFactory.java | 12 +++---- ...ceBindingsLastOperationExecutionTest.java} | 4 +-- ...UnbindingsLastOperationExecutionTest.java} | 8 ++--- ...viceUnbindingsOperationExecutionTest.java} | 4 +-- 13 files changed, 94 insertions(+), 63 deletions(-) delete mode 100644 multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationFailSafeExecution.java rename multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/{PollServiceBindingLastOperationExecution.java => PollServiceBindingsLastOperationExecution.java} (78%) create mode 100644 multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationFailSafeExecution.java rename multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/{PollServiceUnbindingLastOperationExecution.java => PollServiceUnbindingsLastOperationExecution.java} (82%) rename multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/{PollServiceUnbindingOperationExecution.java => PollServiceUnbindingsOperationExecution.java} (84%) rename multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/{PollServiceBindingLastOperationExecutionTest.java => PollServiceBindingsLastOperationExecutionTest.java} (98%) rename multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/{PollServiceUnbindingLastOperationExecutionTest.java => PollServiceUnbindingsLastOperationExecutionTest.java} (92%) rename multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/{PollServiceUnbindingOperationExecutionTest.java => PollServiceUnbindingsOperationExecutionTest.java} (98%) diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java index 765718a49f..29b9319260 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/rest/CloudControllerRestClientImpl.java @@ -2049,23 +2049,19 @@ private List doUnbindServiceInstance(UUID applicationGuid, UUID serviceI private List doDeleteServiceBindings(List guids) { List jobIds = new ArrayList<>(); - List errors = new ArrayList<>(); + List errors = new ArrayList<>(); for (UUID guid : guids) { - handleServiceBindingDeletion(guid, jobIds, errors); + try { + Optional.ofNullable(deleteSingleServiceBinding(guid)) + .ifPresent(jobIds::add); + } catch (AbstractCloudFoundryException e) { + errors.add(e); + } } throwOnErrors(errors); return jobIds; } - private void handleServiceBindingDeletion(UUID guid, List jobIds, List errors) { - try { - Optional.ofNullable(deleteSingleServiceBinding(guid)) - .ifPresent(jobIds::add); - } catch (RuntimeException e) { - errors.add(e); - } - } - private String deleteSingleServiceBinding(UUID guid) { return delegate.serviceBindingsV3() .delete(DeleteServiceBindingRequest.builder() @@ -2074,7 +2070,7 @@ private String deleteSingleServiceBinding(UUID guid) { .block(); } - private void throwOnErrors(List errors) { + private void throwOnErrors(List errors) { if (errors.isEmpty()) { return; } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java index e06960b330..a4420bff96 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/Messages.java @@ -265,8 +265,13 @@ public class Messages { public static final String COULD_NOT_PARSE_APP_STOP_DELAY_VAR = "Could not parse variable for delay after app stop: \"{0}\""; public static final String DELAY_AFTER_APP_STOP_CANNOT_BE_NEGATIVE = "Delay after app stop cannot be negative: \"{0}\""; public static final String SERVICE_BINDING_0_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED = "Service binding \"{0}\" exists in broken state, will be recreated"; + public static final String SERVICE_BINDINGS_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED = "Service bindings \"{0}\" exists in broken state, will be recreated"; public static final String ERROR_WHILE_POLLING_SERVICE_BINDING_OPERATION_BETWEEN_APP_AND_OPTIONAL_SERVICE = "Error while polling service binding operation between app \"{0}\" and optional service instance \"{1}\""; public static final String ERROR_WHILE_POLLING_SERVICE_BINDING_OPERATION_BETWEEN_APP_AND_SERVICE = "Error while polling service binding operation between app \"{0}\" and service instance \"{1}\", error \"{2}\""; + public static final String ERROR_WHILE_POLLING_SERVICE_BINDING_OPERATION_BETWEEN_APP_AND_SERVICE_CHECK_OPERATION_LOGS = + "Error while polling service binding operation between app \"{0}\" and service instance \"{1}\". Multiple bindings error were detected. " + + DOWNLOAD_APP_LOGS_FOR_MORE_INFO; + public static final String ERROR_WHILE_POLLING_SERVICE_BINDING_GUID = "Error while polling service binding guid: \"{0}\", error \"{1}\""; public static final String ERROR_WHILE_CREATING_SERVICE_KEY_0_FOR_OPTIONAL_SERVICE_1 = "Error while creating service key \"{0}\" for optional service \"{1}\""; public static final String ERROR_WHILE_DELETING_SERVICE_KEY_0_FOR_OPTIONAL_SERVICE_1 = "Error while deleting service key \"{0}\" for optional service \"{1}\""; public static final String SERVICE_KEY_0_IS_ALREADY_DELETED = "Service key \"{0}\" is already deleted"; diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java index 8269e1b7c6..2972d915ad 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/CheckServiceBindingOperationStep.java @@ -94,7 +94,7 @@ private StepPhase checkServiceBindingsOperationState(List s @Override protected List getAsyncStepExecutions(ProcessContext context) { - return List.of(new PollServiceBindingLastOperationFailSafeExecution()); + return List.of(new PollServiceBindingsLastOperationFailSafeExecution()); } @Override diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationFailSafeExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationFailSafeExecution.java deleted file mode 100644 index efec0706ff..0000000000 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationFailSafeExecution.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.cloudfoundry.multiapps.controller.process.steps; - -import java.text.MessageFormat; - -import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; -import org.cloudfoundry.multiapps.controller.process.Messages; -import org.cloudfoundry.multiapps.controller.process.variables.Variables; - -public class PollServiceBindingLastOperationFailSafeExecution extends PollServiceBindingLastOperationExecution { - - @Override - protected AsyncExecutionState completePollingOfFailedOperation(CloudServiceBinding serviceBinding, ProcessContext context) { - context.getStepLogger() - .warnWithoutProgressMessage(MessageFormat.format(Messages.SERVICE_BINDING_0_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED, - serviceBinding.getGuid())); - context.setVariable(Variables.SHOULD_RECREATE_SERVICE_BINDING, true); - return AsyncExecutionState.FINISHED; - } -} diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationExecution.java similarity index 78% rename from multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java rename to multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationExecution.java index 24887701c0..106c972d5c 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecution.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationExecution.java @@ -1,6 +1,7 @@ package org.cloudfoundry.multiapps.controller.process.steps; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -14,7 +15,7 @@ import org.cloudfoundry.multiapps.controller.process.util.StepLogger; import org.cloudfoundry.multiapps.controller.process.variables.Variables; -public class PollServiceBindingLastOperationExecution implements AsyncExecution { +public class PollServiceBindingsLastOperationExecution implements AsyncExecution { @Override public AsyncExecutionState execute(ProcessContext context) { @@ -59,7 +60,7 @@ private List getServiceBindingsForAppAndServiceInstance(Pro } protected AsyncExecutionState checkServiceBindingOperationState(List serviceBindings, ProcessContext context) { - CloudServiceBinding failedServiceBinding = null; + List failedServiceBindings = new ArrayList<>(); boolean hasInProgressBindings = false; StepLogger stepLogger = context.getStepLogger(); @@ -68,7 +69,7 @@ protected AsyncExecutionState checkServiceBindingOperationState(List failedServiceBindings, + ProcessContext context) { List servicesToBind = context.getVariable(Variables.SERVICES_TO_BIND); CloudApplication app = context.getVariable(Variables.APP_TO_PROCESS); String serviceInstanceName = context.getVariable(Variables.SERVICE_TO_UNBIND_BIND); @@ -95,10 +97,25 @@ protected AsyncExecutionState completePollingOfFailedOperation(CloudServiceBindi app.getName()); return AsyncExecutionState.FINISHED; } + if (failedServiceBindings.size() == 1) { + CloudServiceBinding serviceBinding = failedServiceBindings.get(0); + context.getStepLogger() + .error(Messages.ERROR_WHILE_POLLING_SERVICE_BINDING_OPERATION_BETWEEN_APP_AND_SERVICE, app.getName(), + serviceInstanceName, + serviceBinding.getServiceBindingOperation() + .getDescription()); + return AsyncExecutionState.ERROR; + } context.getStepLogger() - .error(Messages.ERROR_WHILE_POLLING_SERVICE_BINDING_OPERATION_BETWEEN_APP_AND_SERVICE, app.getName(), serviceInstanceName, - serviceBinding.getServiceBindingOperation() - .getDescription()); + .error(Messages.ERROR_WHILE_POLLING_SERVICE_BINDING_OPERATION_BETWEEN_APP_AND_SERVICE_CHECK_OPERATION_LOGS, app.getName(), + serviceInstanceName); + for (CloudServiceBinding serviceBinding : failedServiceBindings) { + context.getStepLogger() + .errorWithoutProgressMessage(Messages.ERROR_WHILE_POLLING_SERVICE_BINDING_GUID, + serviceBinding.getGuid(), + serviceBinding.getServiceBindingOperation() + .getDescription()); + } return AsyncExecutionState.ERROR; } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationFailSafeExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationFailSafeExecution.java new file mode 100644 index 0000000000..d049e08ccc --- /dev/null +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationFailSafeExecution.java @@ -0,0 +1,29 @@ +package org.cloudfoundry.multiapps.controller.process.steps; + +import java.util.List; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; +import org.cloudfoundry.multiapps.controller.process.Messages; +import org.cloudfoundry.multiapps.controller.process.variables.Variables; + +public class PollServiceBindingsLastOperationFailSafeExecution extends PollServiceBindingsLastOperationExecution { + + @Override + protected AsyncExecutionState completePollingOfFailedOperation(List failedServiceBindings, + ProcessContext context) { + if (failedServiceBindings.size() == 1) { + context.getStepLogger() + .warnWithoutProgressMessage(Messages.SERVICE_BINDING_0_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED, + failedServiceBindings.get(0) + .getGuid()); + } else { + context.getStepLogger() + .warnWithoutProgressMessage(Messages.SERVICE_BINDINGS_EXISTS_IN_BROKEN_STATE_WILL_BE_RECREATED, + failedServiceBindings.stream() + .map(CloudServiceBinding::getGuid) + .toList()); + } + context.setVariable(Variables.SHOULD_RECREATE_SERVICE_BINDING, true); + return AsyncExecutionState.FINISHED; + } +} diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsLastOperationExecution.java similarity index 82% rename from multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java rename to multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsLastOperationExecution.java index b8673f1969..f52246b439 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecution.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsLastOperationExecution.java @@ -4,7 +4,7 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudServiceBinding; -public class PollServiceUnbindingLastOperationExecution extends PollServiceBindingLastOperationExecution { +public class PollServiceUnbindingsLastOperationExecution extends PollServiceBindingsLastOperationExecution { @Override protected AsyncExecutionState checkServiceBindingOperationState(List serviceBindings, ProcessContext context) { diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecution.java similarity index 84% rename from multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java rename to multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecution.java index c94205a0dc..238c5ecf39 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecution.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecution.java @@ -5,12 +5,12 @@ import org.cloudfoundry.multiapps.controller.process.variables.Variables; -public class PollServiceUnbindingOperationExecution extends PollServiceBindingUnbindingOperationBaseExecution { +public class PollServiceUnbindingsOperationExecution extends PollServiceBindingUnbindingOperationBaseExecution { @Override public AsyncExecutionState execute(ProcessContext context) { List jobIds = context.getVariable(Variables.SERVICE_UNBINDING_JOB_IDS); - if (jobIds.isEmpty()) { + if (jobIds.isEmpty() && context.getVariable(Variables.SERVICE_UNBINDING_JOB_ID) != null) { return super.execute(context); } List remainingJobIds = new ArrayList<>(jobIds); diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java index c44fcf6771..c1dc32277c 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingParametersGetter.java @@ -136,13 +136,14 @@ public Map getServiceBindingParametersFromExistingInstance(Cloud UUID serviceInstanceGuid) { CloudControllerClient client = context.getControllerClient(); List serviceBindings = client.getServiceBindingsForApplication(application.getGuid(), serviceInstanceGuid); - CloudServiceBinding serviceBinding = selectNewestBinding(serviceBindings); - if (serviceBinding == null) { + Optional serviceBindingOptional = selectNewestBinding(serviceBindings); + if (serviceBindingOptional.isEmpty()) { throw new SLException(Messages.SERVICE_INSTANCE_0_NOT_BOUND_TO_APP_1, serviceName, application.getName()); } try { - return client.getServiceBindingParameters(serviceBinding.getGuid()); + return client.getServiceBindingParameters(serviceBindingOptional.get() + .getGuid()); } catch (CloudOperationException e) { if (HttpStatus.NOT_IMPLEMENTED == e.getStatusCode() || HttpStatus.BAD_REQUEST == e.getStatusCode()) { // ignore 501 and 400 error codes from service brokers @@ -163,13 +164,15 @@ public Map getServiceBindingParametersFromExistingInstance(Cloud } } - private CloudServiceBinding selectNewestBinding(List bindings) { + private Optional selectNewestBinding(List bindings) { return bindings.stream() .filter(binding -> binding.getServiceBindingOperation() .getState() == ServiceCredentialBindingOperation.State.SUCCEEDED) .max(Comparator.comparing(binding -> binding.getMetadata() .getCreatedAt())) - .orElseGet(() -> bindings.isEmpty() ? null : bindings.get(0)); + .or(() -> bindings.stream() + .max(Comparator.comparing(binding -> binding.getMetadata() + .getCreatedAt()))); } } diff --git a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingPollingFactory.java b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingPollingFactory.java index 155ba5f159..bc560790c2 100644 --- a/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingPollingFactory.java +++ b/multiapps-controller-process/src/main/java/org/cloudfoundry/multiapps/controller/process/util/ServiceBindingPollingFactory.java @@ -2,10 +2,10 @@ import org.cloudfoundry.multiapps.controller.client.facade.domain.ServiceCredentialBindingOperation; import org.cloudfoundry.multiapps.controller.process.steps.AsyncExecution; -import org.cloudfoundry.multiapps.controller.process.steps.PollServiceBindingLastOperationExecution; import org.cloudfoundry.multiapps.controller.process.steps.PollServiceBindingOperationExecution; -import org.cloudfoundry.multiapps.controller.process.steps.PollServiceUnbindingLastOperationExecution; -import org.cloudfoundry.multiapps.controller.process.steps.PollServiceUnbindingOperationExecution; +import org.cloudfoundry.multiapps.controller.process.steps.PollServiceBindingsLastOperationExecution; +import org.cloudfoundry.multiapps.controller.process.steps.PollServiceUnbindingsLastOperationExecution; +import org.cloudfoundry.multiapps.controller.process.steps.PollServiceUnbindingsOperationExecution; import org.cloudfoundry.multiapps.controller.process.steps.ProcessContext; import org.cloudfoundry.multiapps.controller.process.variables.Variables; @@ -28,16 +28,16 @@ public AsyncExecution createPollingExecution() { protected AsyncExecution createPollingExecutionWithTypeCreate() { if (context.getVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_CREATION)) { - return new PollServiceBindingLastOperationExecution(); + return new PollServiceBindingsLastOperationExecution(); } return new PollServiceBindingOperationExecution(); } protected AsyncExecution createPollingExecutionWithTypeDelete() { if (context.getVariable(Variables.USE_LAST_OPERATION_FOR_SERVICE_BINDING_DELETION)) { - return new PollServiceUnbindingLastOperationExecution(); + return new PollServiceUnbindingsLastOperationExecution(); } - return new PollServiceUnbindingOperationExecution(); + return new PollServiceUnbindingsOperationExecution(); } protected ProcessContext getContext() { diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationExecutionTest.java similarity index 98% rename from multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java rename to multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationExecutionTest.java index 31277416e4..267f7817ce 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingLastOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceBindingsLastOperationExecutionTest.java @@ -27,7 +27,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; -class PollServiceBindingLastOperationExecutionTest extends AsyncStepOperationTest { +class PollServiceBindingsLastOperationExecutionTest extends AsyncStepOperationTest { private static final UUID SERVICE_BINDING_APP_GUID = UUID.randomUUID(); private static final UUID SERVICE_BINDING_SERVICE_INSTANCE_GUID = UUID.randomUUID(); @@ -159,7 +159,7 @@ private CloudApplicationExtended buildCloudApplication() { @Override protected List getAsyncOperations(ProcessContext wrapper) { - return List.of(new PollServiceBindingLastOperationExecution()); + return List.of(new PollServiceBindingsLastOperationExecution()); } @Override diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsLastOperationExecutionTest.java similarity index 92% rename from multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecutionTest.java rename to multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsLastOperationExecutionTest.java index 3c2fc57d4f..1be541122c 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingLastOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsLastOperationExecutionTest.java @@ -1,7 +1,5 @@ package org.cloudfoundry.multiapps.controller.process.steps; -import static org.junit.jupiter.api.Assertions.assertEquals; - import java.util.List; import org.cloudfoundry.multiapps.controller.client.lib.domain.CloudApplicationExtended; @@ -11,7 +9,9 @@ import org.cloudfoundry.multiapps.controller.process.variables.Variables; import org.junit.jupiter.api.Test; -class PollServiceUnbindingLastOperationExecutionTest extends AsyncStepOperationTest { +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PollServiceUnbindingsLastOperationExecutionTest extends AsyncStepOperationTest { private static final String APP_TO_PROCESS_NAME = "test-app"; private static final String SERVICE_TO_UNBIND_BIND = "test-service-instance"; @@ -45,7 +45,7 @@ private CloudApplicationExtended buildCloudApplication() { @Override protected List getAsyncOperations(ProcessContext wrapper) { - return List.of(new PollServiceUnbindingLastOperationExecution()); + return List.of(new PollServiceUnbindingsLastOperationExecution()); } @Override diff --git a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java similarity index 98% rename from multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java rename to multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java index 5008cb1269..671c01c888 100644 --- a/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingOperationExecutionTest.java +++ b/multiapps-controller-process/src/test/java/org/cloudfoundry/multiapps/controller/process/steps/PollServiceUnbindingsOperationExecutionTest.java @@ -25,7 +25,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -class PollServiceUnbindingOperationExecutionTest extends AsyncStepOperationTest { +class PollServiceUnbindingsOperationExecutionTest extends AsyncStepOperationTest { private static final UUID JOB_GUID = UUID.fromString("00000000-0000-0000-0000-000000000123"); private static final String JOB_ID = JOB_GUID.toString(); @@ -315,7 +315,7 @@ private CloudServiceInstance buildManagedServiceInstanceWithNullFields() { @Override protected List getAsyncOperations(ProcessContext wrapper) { - return List.of(new PollServiceUnbindingOperationExecution()); + return List.of(new PollServiceUnbindingsOperationExecution()); } @Override