package alien4cloud.application;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.SettableFuture;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.dao.model.GetMultipleDataResult;
import alien4cloud.deployment.DeploymentRuntimeStateService;
import alien4cloud.deployment.DeploymentService;
import alien4cloud.deployment.DeploymentTopologyService;
import alien4cloud.events.DeleteEnvironmentEvent;
import alien4cloud.exception.AlreadyExistException;
import alien4cloud.exception.DeleteDeployedException;
import alien4cloud.exception.NotFoundException;
import alien4cloud.model.application.ApplicationEnvironment;
import alien4cloud.model.application.ApplicationVersion;
import alien4cloud.model.application.EnvironmentType;
import alien4cloud.model.deployment.Deployment;
import alien4cloud.paas.IPaaSCallback;
import alien4cloud.paas.exception.OrchestratorDisabledException;
import alien4cloud.paas.model.DeploymentStatus;
import alien4cloud.security.model.ApplicationEnvironmentRole;
import alien4cloud.security.model.ApplicationRole;
import alien4cloud.utils.MapUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class ApplicationEnvironmentService {
private final static String DEFAULT_ENVIRONMENT_NAME = "Environment";
@Resource(name = "alien-es-dao")
private IGenericSearchDAO alienDAO;
@Inject
private ApplicationService applicationService;
@Inject
private ApplicationVersionService applicationVersionService;
@Inject
private DeploymentRuntimeStateService deploymentRuntimeStateService;
@Inject
private DeploymentTopologyService deploymentTopologyService;
@Resource
private ApplicationContext applicationContext;
@Inject
private DeploymentService deploymentService;
/**
* Method used to create a default environment
*
* @param applicationId The id of the application for which to create the environment.
* @return The id of the newly created environment.
*/
public ApplicationEnvironment createApplicationEnvironment(String user, String applicationId, String versionId) {
return createApplicationEnvironment(user, applicationId, DEFAULT_ENVIRONMENT_NAME, null, EnvironmentType.OTHER, versionId);
}
/**
* Create a new environment for a given application
*
* @param applicationId The id of the application.
* @param name The environment name.
* @param description The environment description.
* @param environmentType The type of environment.
* @return The newly created environment.
*/
public ApplicationEnvironment createApplicationEnvironment(String user, String applicationId, String name, String description,
EnvironmentType environmentType, String versionId) {
// unique app env name for a given app
ensureNameUnicity(applicationId, name);
ApplicationEnvironment applicationEnvironment = new ApplicationEnvironment();
applicationEnvironment.setId(UUID.randomUUID().toString());
applicationEnvironment.setName(name);
applicationEnvironment.setDescription(description);
applicationEnvironment.setEnvironmentType(environmentType);
applicationEnvironment.setApplicationId(applicationId);
applicationEnvironment.setCurrentVersionId(versionId);
Map<String, Set<String>> userRoles = Maps.newHashMap();
userRoles.put(user, Sets.newHashSet(ApplicationEnvironmentRole.DEPLOYMENT_MANAGER.toString()));
applicationEnvironment.setUserRoles(userRoles);
alienDAO.save(applicationEnvironment);
return applicationEnvironment;
}
/**
* Get all environments for a given application
*
* @param applicationId The id of the application for which to get environments.
* @return An array of the environments for the requested application id.
*/
public ApplicationEnvironment[] getByApplicationId(String applicationId) {
GetMultipleDataResult<ApplicationEnvironment> result = alienDAO.find(ApplicationEnvironment.class,
MapUtil.newHashMap(new String[] { "applicationId" }, new String[][] { new String[] { applicationId } }), Integer.MAX_VALUE);
return result.getData();
}
/**
* Get all environments for a given application
*
* @param versionId The id of the application for which to get environments.
* @return An array of the environments for the requested application id.
*/
public ApplicationEnvironment[] getByVersionId(String versionId) {
GetMultipleDataResult<ApplicationEnvironment> result = alienDAO.find(ApplicationEnvironment.class,
MapUtil.newHashMap(new String[] { "currentVersionId" }, new String[][] { new String[] { versionId } }), Integer.MAX_VALUE);
return result.getData();
}
/**
* Delete a version and the related topologies.
*
* @param id The id of the version to delete.
*/
public boolean delete(String id) {
ApplicationEnvironment environmentToDelete = getOrFail(id);
boolean isDeployed = isDeployed(id);
if (isDeployed) {
throw new DeleteDeployedException("Application environment with id <" + id + "> cannot be deleted since it is deployed");
}
deploymentTopologyService.deleteByEnvironmentId(id);
alienDAO.delete(ApplicationEnvironment.class, id);
applicationContext
.publishEvent(new DeleteEnvironmentEvent(this, environmentToDelete, deploymentService.getAllOrchestratorIdsAndOrchestratorDeploymentId(id)));
return true;
}
/**
* Delete all environments related to an application
*
* @param applicationId The application id
* @throws alien4cloud.paas.exception.OrchestratorDisabledException
*/
public void deleteByApplication(String applicationId) throws OrchestratorDisabledException {
List<String> deployedEnvironments = Lists.newArrayList();
ApplicationEnvironment[] environments = getByApplicationId(applicationId);
for (ApplicationEnvironment environment : environments) {
if (!this.isDeployed(environment.getId())) {
delete(environment.getId());
} else {
// collect all deployed environment
deployedEnvironments.add(environment.getId());
}
}
// couln't delete deployed environment
if (deployedEnvironments.size() > 0) {
// error could not deployed all app environment for this applcation
log.error("Cannot delete these deployed environments : {}", deployedEnvironments.toString());
}
}
/**
* Get an active deployment associated with an environment.
*
* @param appEnvironmentId The id of the environment for which to get an active deployment.
* @return The deployment associated with the environment.
*/
public Deployment getActiveDeployment(String appEnvironmentId) {
GetMultipleDataResult<Deployment> dataResult = alienDAO.search(Deployment.class, null,
MapUtil.newHashMap(new String[] { "environmentId", "endDate" }, new String[][] { new String[] { appEnvironmentId }, new String[] { null } }),
1);
if (dataResult.getData() != null && dataResult.getData().length > 0) {
return dataResult.getData()[0];
}
return null;
}
/**
* True when an application environment is deployed
*
* @return true if the environment is currently deployed
*/
public boolean isDeployed(String appEnvironmentId) {
if (getActiveDeployment(appEnvironmentId) == null) {
return false;
}
return true;
}
/**
* Get an application environment from it's id and throw a {@link NotFoundException} in case no application environment matches the requested id
*
* @param applicationEnvironmentId
* @return The requested application environment
*/
public ApplicationEnvironment getOrFail(String applicationEnvironmentId) {
ApplicationEnvironment applicationEnvironment = alienDAO.findById(ApplicationEnvironment.class, applicationEnvironmentId);
if (applicationEnvironment == null) {
throw new NotFoundException("Application environment [" + applicationEnvironmentId + "] cannot be found");
}
return applicationEnvironment;
}
/**
* Check the the name of the application environment is already used
*
* @param name The name of the application environment
* @return true if an application environment already use this name, false if not
*/
public void ensureNameUnicity(String applicationId, String name) {
long result = alienDAO.count(ApplicationEnvironment.class, null,
MapUtil.newHashMap(new String[] { "applicationId", "name" }, new String[][] { new String[] { applicationId }, new String[] { name } }));
if (result > 0) {
log.debug("Application environment with name <{}> already exists for application id <{}>", name, applicationId);
throw new AlreadyExistException("An application environment with the given name already exists");
}
}
/**
* Check rights on the related application and get the application environment
* If no roles mentioned, all {@link ApplicationRole} values will be used
*
* @param applicationEnvironmentId
* @param roles {@link ApplicationRole} to check right on the underlying application
* @return the corresponding application environment
*/
public ApplicationEnvironment checkAndGetApplicationEnvironment(String applicationEnvironmentId, ApplicationRole... roles) {
ApplicationEnvironment applicationEnvironment = getOrFail(applicationEnvironmentId);
applicationService.checkAndGetApplication(applicationEnvironment.getApplicationId(), roles);
return applicationEnvironment;
}
/**
* Get the environment status regarding the linked topology and cloud
*
* @param environment to determine the status
* @return {@link DeploymentStatus}
* @throws alien4cloud.paas.exception.OrchestratorDisabledException
*/
public DeploymentStatus getStatus(ApplicationEnvironment environment) throws Exception {
final Deployment deployment = getActiveDeployment(environment.getId());
if (deployment == null) {
return DeploymentStatus.UNDEPLOYED;
}
final SettableFuture<DeploymentStatus> statusSettableFuture = SettableFuture.create();
// update the deployment status from PaaS if it cannot be found.
deploymentRuntimeStateService.getDeploymentStatus(deployment, new IPaaSCallback<DeploymentStatus>() {
@Override
public void onSuccess(DeploymentStatus data) {
statusSettableFuture.set(data);
}
@Override
public void onFailure(Throwable throwable) {
statusSettableFuture.setException(throwable);
}
});
DeploymentStatus currentStatus = statusSettableFuture.get();
if (DeploymentStatus.UNDEPLOYED.equals(currentStatus)) {
deploymentService.markUndeployed(deployment);
}
return currentStatus;
}
/**
* Get the topology id linked to the environment
*
* @param applicationEnvironmentId The id of the environment.
* @return a topology id or null
*/
public String getTopologyId(String applicationEnvironmentId) {
ApplicationEnvironment applicationEnvironment = getOrFail(applicationEnvironmentId);
ApplicationVersion applicationVersion = applicationVersionService.get(applicationEnvironment.getCurrentVersionId());
return applicationVersion == null ? null : applicationVersion.getId();
}
/**
* Get a environment for and application
*
* @param applicationId
* @param applicationEnvironmentId
* @return
*/
public ApplicationEnvironment getEnvironmentByIdOrDefault(String applicationId, String applicationEnvironmentId) {
ApplicationEnvironment environment = null;
if (applicationEnvironmentId == null) {
ApplicationEnvironment[] applicationEnvironments = getByApplicationId(applicationId);
environment = applicationEnvironments[0];
} else {
environment = getOrFail(applicationEnvironmentId);
}
return environment;
}
}