package org.cloudfoundry.community.servicebroker.datalifecycle.service; import static org.cloudfoundry.community.servicebroker.datalifecycle.config.LCCatalogConfig.COPY; import static org.cloudfoundry.community.servicebroker.datalifecycle.config.LCCatalogConfig.PRODUCTION; import static org.cloudfoundry.community.servicebroker.datalifecycle.model.BrokerActionState.COMPLETE; import static org.cloudfoundry.community.servicebroker.datalifecycle.model.BrokerActionState.FAILED; import static org.cloudfoundry.community.servicebroker.datalifecycle.model.BrokerActionState.IN_PROGRESS; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.log4j.Logger; import org.cloudfoundry.community.servicebroker.datalifecycle.dto.InstancePair; import org.cloudfoundry.community.servicebroker.datalifecycle.model.BrokerAction; import org.cloudfoundry.community.servicebroker.datalifecycle.model.BrokerActionState; import org.cloudfoundry.community.servicebroker.datalifecycle.provider.CopyProvider; import org.cloudfoundry.community.servicebroker.datalifecycle.provider.DataProvider; import org.cloudfoundry.community.servicebroker.datalifecycle.repo.BrokerActionRepository; import org.cloudfoundry.community.servicebroker.exception.ServiceBrokerAsyncRequiredException; import org.cloudfoundry.community.servicebroker.exception.ServiceBrokerException; import org.cloudfoundry.community.servicebroker.exception.ServiceInstanceDoesNotExistException; import org.cloudfoundry.community.servicebroker.exception.ServiceInstanceExistsException; import org.cloudfoundry.community.servicebroker.exception.ServiceInstanceUpdateNotSupportedException; import org.cloudfoundry.community.servicebroker.model.CreateServiceInstanceRequest; import org.cloudfoundry.community.servicebroker.model.DeleteServiceInstanceRequest; import org.cloudfoundry.community.servicebroker.model.OperationState; import org.cloudfoundry.community.servicebroker.model.ServiceInstance; import org.cloudfoundry.community.servicebroker.model.ServiceInstanceLastOperation; import org.cloudfoundry.community.servicebroker.model.UpdateServiceInstanceRequest; import org.cloudfoundry.community.servicebroker.service.ServiceInstanceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.task.TaskExecutor; import org.springframework.stereotype.Service; /** * Creating a service instance is a no op for the copy operations, we simply * assume that the prod instance exists. * * @author jkruck * */ @Service public class LCServiceInstanceService implements ServiceInstanceService { private Logger logger = Logger.getLogger(LCServiceInstanceService.class); private LCServiceInstanceManager instanceManager; private CopyProvider copyProvider; private String sourceInstanceId; private BrokerActionRepository brokerRepo; private TaskExecutor executor; private DataProvider dataProvider; private DataProviderService dataProviderService; @Autowired public LCServiceInstanceService( final CopyProvider copyProvider, final DataProvider dataProvider, @Value("#{environment.SOURCE_INSTANCE_ID}") final String sourceInstanceId, final BrokerActionRepository brokerRepo, final LCServiceInstanceManager instanceManager, final TaskExecutor executor, final DataProviderService dataProviderService) { this.copyProvider = copyProvider; this.dataProvider = dataProvider; this.sourceInstanceId = sourceInstanceId; this.brokerRepo = brokerRepo; this.instanceManager = instanceManager; this.executor = executor; this.dataProviderService = dataProviderService; } @Override public ServiceInstance createServiceInstance( CreateServiceInstanceRequest request) throws ServiceInstanceExistsException, ServiceBrokerException, ServiceBrokerAsyncRequiredException { String id = request.getServiceInstanceId(); log(id, "Creating service instance", IN_PROGRESS); throwIfDuplicate(id); throwIfSync(request); ServiceInstance instance = null; if (PRODUCTION.equals(request.getPlanId())) { instance = new ServiceInstance(request).isAsync(false) .withLastOperation( new ServiceInstanceLastOperation("Provisioned", OperationState.SUCCEEDED)); instanceManager.saveInstance(instance, sourceInstanceId); } else { instance = new ServiceInstance(request).isAsync(true) .withLastOperation( new ServiceInstanceLastOperation( "Creating instance", OperationState.IN_PROGRESS)); instanceManager.saveInstance(instance, null); provision(request, id, instance); } return instance; } private void provision(CreateServiceInstanceRequest request, String id, ServiceInstance instance) { executor.execute(new Runnable() { @Override public void run() { try { String copyId = sourceInstanceId; copyId = copyProvider.createCopy(sourceInstanceId); logger.info("Sanitizing copy " + copyId); String script = dataProviderService.getScript(); Map<String, Object> creds = copyProvider.getCreds(copyId); dataProvider.sanitize(script, creds); instance.withLastOperation(new ServiceInstanceLastOperation( "Provisioned", OperationState.SUCCEEDED)); instanceManager.saveInstance(instance, copyId); log(id, "Created service instance", COMPLETE); } catch (Exception e) { instance.withLastOperation(new ServiceInstanceLastOperation( e.getMessage(), OperationState.FAILED)); instanceManager.saveInstance(instance, null); log(id, "Failed to create service instance: " + e.getMessage(), FAILED); } } }); } @Override public ServiceInstance deleteServiceInstance( DeleteServiceInstanceRequest request) throws ServiceBrokerException, ServiceBrokerAsyncRequiredException { throwIfSync(request); String id = request.getServiceInstanceId(); log(id, "Deleting service instance", IN_PROGRESS); ServiceInstance instance = instanceManager.getInstance(id); if (null == instance) { log(id, "Service instance not found", FAILED); return null; } String copyId = instanceManager.getCopyIdForInstance(id); instanceManager.saveInstance( instance.withLastOperation( new ServiceInstanceLastOperation("deprovisioning", OperationState.IN_PROGRESS)).isAsync(true), copyId); deProvision(request, id, instance); return instance; } private void deProvision(DeleteServiceInstanceRequest request, String id, ServiceInstance instance) { executor.execute(new Runnable() { @Override public void run() { try { if (COPY.equals(request.getPlanId())) { copyProvider.deleteCopy(instanceManager .getCopyIdForInstance(id)); } log(id, "Deleted service instance", COMPLETE); instanceManager.removeInstance(id); } catch (ServiceBrokerException e) { log(id, "Failed to delete service instance: " + e.getMessage(), FAILED); instance.withLastOperation(new ServiceInstanceLastOperation( "failed to delete", OperationState.FAILED)); String copyId = instanceManager.getCopyIdForInstance(id); instanceManager.saveInstance(instance, copyId); } } }); } @Override public ServiceInstance getServiceInstance(String id) { return instanceManager.getInstance(id); } @Override public ServiceInstance updateServiceInstance( UpdateServiceInstanceRequest request) throws ServiceInstanceUpdateNotSupportedException, ServiceBrokerException, ServiceInstanceDoesNotExistException { log(request.getServiceInstanceId(), "Updating service instance is not supported", FAILED); throw new ServiceInstanceUpdateNotSupportedException( "Cannot update plan " + request.getPlanId()); } public String getInstanceIdForServiceInstance(String serviceInstanceId) { // @formatter:off return instanceManager .getInstances() .stream() .filter(s -> s.getRight().getServiceInstanceId() .equals(serviceInstanceId)).findFirst().get().getLeft(); // @formatter:on } public List<InstancePair> getProvisionedInstances() { // @formatter:off return instanceManager.getInstances().stream() .map(i -> new InstancePair(sourceInstanceId, i.getLeft())) .collect(Collectors.toList()); // @formatter:on } public String getSourceInstanceId() { return sourceInstanceId; } private void log(String id, String msg, BrokerActionState state) { String logMsg = msg + " " + id; if (FAILED == state) { logger.error(logMsg); } else { logger.info(logMsg); } brokerRepo.save(new BrokerAction(id, state, msg)); } private void throwIfDuplicate(String id) throws ServiceInstanceExistsException { if (null != instanceManager.getInstance(id)) { log(id, "Duplicate service instance requested", FAILED); throw new ServiceInstanceExistsException( instanceManager.getInstance(id)); } } private void throwIfSync(CreateServiceInstanceRequest request) throws ServiceBrokerAsyncRequiredException { if (!request.hasAsyncClient()) { throw new ServiceBrokerAsyncRequiredException( "Lifecycle broker requires an async CloudController"); } } private void throwIfSync(DeleteServiceInstanceRequest request) throws ServiceBrokerAsyncRequiredException { if (!request.hasAsyncClient()) { throw new ServiceBrokerAsyncRequiredException( "Lifecycle broker requires an async CloudController"); } } }