package alien4cloud.rest.deployment; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import javax.inject.Inject; import javax.validation.Valid; import io.swagger.annotations.Api; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.ArrayUtils; import org.elasticsearch.common.collect.Lists; import org.hibernate.validator.constraints.NotBlank; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import alien4cloud.application.ApplicationService; import alien4cloud.audit.annotation.Audit; import alien4cloud.dao.IGenericSearchDAO; import alien4cloud.dao.model.FetchContext; import alien4cloud.dao.model.GetMultipleDataResult; import alien4cloud.deployment.DeploymentRuntimeStateService; import alien4cloud.deployment.DeploymentService; import alien4cloud.deployment.UndeployService; import alien4cloud.model.deployment.Deployment; import alien4cloud.model.deployment.DeploymentSourceType; import alien4cloud.model.deployment.IDeploymentSource; import alien4cloud.model.orchestrators.locations.Location; import alien4cloud.orchestrators.locations.services.LocationService; import alien4cloud.paas.IPaaSCallback; import alien4cloud.paas.exception.OrchestratorDisabledException; import alien4cloud.paas.model.DeploymentStatus; import alien4cloud.rest.model.RestError; import alien4cloud.rest.model.RestErrorCode; import alien4cloud.rest.model.RestResponse; import alien4cloud.rest.model.RestResponseBuilder; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import io.swagger.annotations.Authorization; @RestController @RequestMapping({ "/rest/deployments", "/rest/v1/deployments", "/rest/latest/deployments" }) @Api(value = "", description = "Operations on Deployments") public class DeploymentController { @Resource(name = "alien-es-dao") private IGenericSearchDAO alienDAO; @Resource private DeploymentService deploymentService; @Resource private ApplicationService applicationService; @Inject private DeploymentRuntimeStateService deploymentRuntimeStateService; @Inject private UndeployService undeployService; @Inject private LocationService locationService; /** * Get all deployments for a cloud, including if asked some details of the related applications. * * @param orchestratorId Id of the orchestrator for which to get deployments (can be null to get deployments for all orchestrators). * @param sourceId Id of the application for which to get deployments (can be null to get deployments for all applications). * @param includeSourceSummary include or not the sources (application or csar) summary in the results. * @return A {@link RestResponse} with as data a list of {@link DeploymentDTO} that contains deployments and applications info. */ @ApiOperation(value = "Get deployments for an orchestrator.", authorizations = { @Authorization("ADMIN") }) @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public RestResponse<List<DeploymentDTO>> get( @ApiParam(value = "Id of the orchestrator for which to get deployments. If not provided, get deployments for all orchestrators") @RequestParam(required = false) String orchestratorId, @ApiParam(value = "Id of the application for which to get deployments. if not provided, get deployments for all applications") @RequestParam(required = false) String sourceId, @ApiParam(value = "include or not the source (application or csar) summary in the results") @RequestParam(required = false, defaultValue = "false") boolean includeSourceSummary) { return RestResponseBuilder.<List<DeploymentDTO>> builder().data(buildDeploymentsDTO(orchestratorId, sourceId, includeSourceSummary)).build(); } /** * Get deployments for a given orchestrator, and some info about the related applications and locations * * @param orchestratorId Id of the orchestrator for which to get deployments (can be null to get deployments for all orchestrator). * @param sourceId Id of the application for which to get deployments (can be null to get deployments for all applications). * @param includeSourceSummary include or not the applications summary in the results. * @return A list of {@link DeploymentDTO} that contains deployments and applications info. */ private List<DeploymentDTO> buildDeploymentsDTO(String orchestratorId, String sourceId, boolean includeSourceSummary) { List<Deployment> deployments = deploymentService.getDeployments(orchestratorId, sourceId); List<DeploymentDTO> dtos = Lists.newArrayList(); if (deployments != null && deployments.size() > 0) { Map<String, ? extends IDeploymentSource> sources = Maps.newHashMap(); // get the app summaries if true if (includeSourceSummary) { DeploymentSourceType sourceType = deployments.get(0).getSourceType(); String[] sourceIds = getSourceIdsFromDeployments(deployments); if (sourceIds[0] != null) { // can have no application deployed switch (sourceType) { case APPLICATION: Map<String, ? extends IDeploymentSource> appSources = applicationService.findByIdsIfAuthorized(FetchContext.SUMMARY, sourceIds); if (appSources != null) { sources = appSources; } // FIXME Allow CSAR deployment again for testing and validation purpose. // break; // case CSAR: // Map<String, ? extends IDeploymentSource> csarSources = csarService.findByIds(FetchContext.SUMMARY, sourceIds); // if (csarSources != null) { // sources = csarSources; // } } } } Map<String, Location> locationsSummariesMap = getRelatedLocationsSummaries(deployments); for (Object object : deployments) { Deployment deployment = (Deployment) object; IDeploymentSource source = sources.get(deployment.getSourceId()); if (source == null) { source = new DeploymentSourceDTO(deployment.getSourceId(), deployment.getSourceName()); } List<Location> locationsSummaries = getLocations(deployment.getLocationIds(), locationsSummariesMap); DeploymentDTO dto = new DeploymentDTO(deployment, source, locationsSummaries); dtos.add(dto); } } return dtos; } private Map<String, Location> getRelatedLocationsSummaries(List<Deployment> deployments) { Set<String> locationIds = Sets.newHashSet(); for (Deployment deployment : deployments) { if (ArrayUtils.isNotEmpty(deployment.getLocationIds())) { locationIds.addAll(Sets.newHashSet(deployment.getLocationIds())); } } Map<String, Location> locations = null; if (!locationIds.isEmpty()) { locations = locationService.findByIdsIfAuthorized(FetchContext.SUMMARY, locationIds.toArray(new String[0])); } return locations != null ? locations : Maps.<String, Location> newHashMap(); } private List<Location> getLocations(String[] locationIds, Map<String, Location> locationSummaries) { if (MapUtils.isEmpty(locationSummaries) || ArrayUtils.isEmpty(locationIds)) { return null; } List<Location> locations = Lists.newArrayList(); for (String id : locationIds) { if (locationSummaries.containsKey(id)) { locations.add(locationSummaries.get(id)); } } return locations; } private String[] getSourceIdsFromDeployments(List<Deployment> deployments) { List<String> sourceIds = new ArrayList<>(deployments.size()); for (Deployment deployment : deployments) { if (deployment.getSourceId() != null) {// applicationId nul in "deployment" test case sourceIds.add(deployment.getSourceId()); } } return sourceIds.toArray(new String[sourceIds.size()]); } @RequestMapping(value = "/{applicationEnvironmentId}/events", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public RestResponse<GetMultipleDataResult> getEvents( @ApiParam(value = "Id of the environment for which to get events.", required = true) @Valid @NotBlank @PathVariable String applicationEnvironmentId, @ApiParam(value = "Query from the given index.") @RequestParam(required = false, defaultValue = "0") int from, @ApiParam(value = "Maximum number of results to retrieve.") @RequestParam(required = false, defaultValue = "50") int size) { return RestResponseBuilder.<GetMultipleDataResult> builder() .data(deploymentRuntimeStateService.getDeploymentEvents(applicationEnvironmentId, from, size)).build(); } @ApiOperation(value = "Get deployment status from its id.", authorizations = { @Authorization("ADMIN"), @Authorization("APPLICATION_MANAGER") }) @RequestMapping(value = "/{deploymentId}/status", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public DeferredResult<RestResponse<DeploymentStatus>> getDeploymentStatus( @ApiParam(value = "Deployment id.", required = true) @Valid @NotBlank @PathVariable String deploymentId) { Deployment deployment = alienDAO.findById(Deployment.class, deploymentId); final DeferredResult<RestResponse<DeploymentStatus>> statusResult = new DeferredResult<>(5L * 60L * 1000L); if (deployment != null) { try { deploymentRuntimeStateService.getDeploymentStatus(deployment, new IPaaSCallback<DeploymentStatus>() { @Override public void onSuccess(DeploymentStatus result) { statusResult.setResult(RestResponseBuilder.<DeploymentStatus> builder().data(result).build()); } @Override public void onFailure(Throwable t) { statusResult.setErrorResult(t); } }); return statusResult; } catch (OrchestratorDisabledException e) { statusResult.setResult(RestResponseBuilder.<DeploymentStatus> builder().data(null) .error(new RestError(RestErrorCode.CLOUD_DISABLED_ERROR.getCode(), e.getMessage())).build()); } } else { statusResult.setResult(RestResponseBuilder.<DeploymentStatus> builder().data(null) .error(new RestError(RestErrorCode.NOT_FOUND_ERROR.getCode(), "Deployment with id <" + deploymentId + "> was not found.")).build()); } return statusResult; } @ApiOperation(value = "Undeploy deployment from its id.", authorizations = { @Authorization("ADMIN"), @Authorization("APPLICATION_MANAGER") }) @RequestMapping(value = "/{deploymentId}/undeploy", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") @Audit public RestResponse<Void> undeploy(@ApiParam(value = "Deployment id.", required = true) @Valid @NotBlank @PathVariable String deploymentId) { // Check topology status for this deployment object Deployment deployment = alienDAO.findById(Deployment.class, deploymentId); if (deployment != null) { try { // Undeploy the topology linked to this deployment undeployService.undeploy(deploymentId); } catch (OrchestratorDisabledException e) { return RestResponseBuilder.<Void> builder().data(null).error(new RestError(RestErrorCode.CLOUD_DISABLED_ERROR.getCode(), e.getMessage())) .build(); } } else { return RestResponseBuilder.<Void> builder().data(null) .error(new RestError(RestErrorCode.NOT_FOUND_ERROR.getCode(), "Deployment with id <" + deploymentId + "> was not found.")).build(); } return RestResponseBuilder.<Void> builder().data(null).build(); } }