package org.alien4cloud.tosca.catalog.index;
import static alien4cloud.dao.FilterUtil.fromKeyValueCouples;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.alien4cloud.tosca.catalog.ArchiveDelegateType;
import org.alien4cloud.tosca.catalog.events.AfterArchiveDeleted;
import org.alien4cloud.tosca.catalog.events.BeforeArchiveDeleted;
import org.alien4cloud.tosca.catalog.repository.CsarFileRepository;
import org.alien4cloud.tosca.model.CSARDependency;
import org.alien4cloud.tosca.model.Csar;
import org.alien4cloud.tosca.model.templates.Topology;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import alien4cloud.application.ApplicationService;
import alien4cloud.common.AlienConstants;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.dao.model.GetMultipleDataResult;
import alien4cloud.exception.DeleteReferencedObjectException;
import alien4cloud.exception.NotFoundException;
import alien4cloud.model.application.Application;
import alien4cloud.model.common.Usage;
import alien4cloud.model.orchestrators.locations.Location;
import lombok.extern.slf4j.Slf4j;
/**
* Manages cloud services archives and their dependencies.
*/
@Component
@Slf4j
public class CsarService {
@Inject
private ApplicationEventPublisher publisher;
@Resource(name = "alien-es-dao")
private IGenericSearchDAO csarDAO;
@Inject
private IToscaTypeIndexerService indexerService;
@Inject
private CsarFileRepository alienRepository;
@Inject
private ApplicationService applicationService;
/**
* Get all archive matching the given set of filters.
*
* @param filters The filters to query the archives.
* @param name The name of the archive.
* @return Return the matching
*/
public long count(Map<String, String[]> filters, String name) {
return csarDAO.buildQuery(Csar.class).setFilters(fromKeyValueCouples(filters, "workspace", AlienConstants.GLOBAL_WORKSPACE_ID, "name", name)).count();
}
/**
* Get a cloud service archive.
*
* @param name The name of the archive.
* @param version The version of the archive.
* @return The {@link Csar Cloud Service Archive} if found in the repository or null.
*/
public Csar get(String name, String version) {
return csarDAO.buildQuery(Csar.class).setFilters(fromKeyValueCouples("name", name, "version", version)).prepareSearch().find();
}
/**
*
* Get a cloud service archive.
*
* @param id The id of the archive to retrieve
* @return
*/
public Csar get(String id) {
return csarDAO.findById(Csar.class, id);
}
/**
* @return an array of CSARs that depend on this name:version.
*/
public Csar[] getDependantCsars(String name, String version) {
FilterBuilder filter = FilterBuilders.nestedFilter("dependencies", FilterBuilders.boolFilter()
.must(FilterBuilders.termFilter("dependencies.name", name)).must(FilterBuilders.termFilter("dependencies.version", version)));
GetMultipleDataResult<Csar> result = csarDAO.search(Csar.class, null, null, filter, null, 0, Integer.MAX_VALUE);
return result.getData();
}
/**
* Get teh topologies that depends on this csar.
* Do not return a topology if this csar is his own
*
* @return an array of <code>Topology</code>s that depend on this name:version.
*/
public Topology[] getDependantTopologies(String name, String version) {
FilterBuilder filter = FilterBuilders.boolFilter()
.mustNot(FilterBuilders.boolFilter().must(FilterBuilders.termFilter("archiveName", name))
.must(FilterBuilders.termFilter("archiveVersion", version)))
.must(FilterBuilders.nestedFilter("dependencies", FilterBuilders.boolFilter().must(FilterBuilders.termFilter("dependencies.name", name))
.must(FilterBuilders.termFilter("dependencies.version", version))));
GetMultipleDataResult<Topology> result = csarDAO.search(Topology.class, null, null, filter, null, 0, Integer.MAX_VALUE);
return result.getData();
}
public List<Csar> getTopologiesCsar(Topology... topologies) {
Set<String> ids = Sets.newHashSet();
for (Topology topology : topologies) {
ids.add(topology.getId());
}
return csarDAO.findByIds(Csar.class, ids.toArray(new String[ids.size()]));
}
/**
* @return an array of CSARs that depend on this name:version.
*/
public Location[] getDependantLocations(String name, String version) {
FilterBuilder filter = FilterBuilders.nestedFilter("dependencies", FilterBuilders.boolFilter()
.must(FilterBuilders.termFilter("dependencies.name", name)).must(FilterBuilders.termFilter("dependencies.version", version)));
GetMultipleDataResult<Location> result = csarDAO.search(Location.class, null, null, filter, null, 0, Integer.MAX_VALUE);
return result.getData();
}
/**
* Save a Cloud Service Archive in ElasticSearch.
*
* @param csar The csar to save.
*/
public void save(Csar csar) {
// save the csar import date
csar.setImportDate(new Date());
this.csarDAO.save(csar);
}
/**
* Set dependencies to an existing CSAR
*
* @param csarId id of the CSAR
* @param dependencies the new dependencies
*/
public void setDependencies(String csarId, Set<CSARDependency> dependencies) {
Csar csar = getOrFail(csarId);
csar.setDependencies(dependencies);
save(csar);
}
/**
* Get a cloud service archive, or fail if not found
*
* @param id The id of the archive to retrieve
* @return The {@link Csar Cloud Service Archive} if found in the repository
*/
public Csar getOrFail(String id) {
Csar csar = get(id);
if (csar == null) {
throw new NotFoundException("Csar with id [" + id + "] do not exist");
}
return csar;
}
/**
* Get a cloud service archive, or fail with {@link NotFoundException} if not found
*
* @param name The name of the archive.
* @param version The version of the archive.
* @return The {@link Csar Cloud Service Archive} if found in the repository.
*/
public Csar getOrFail(String name, String version) {
return getOrFail(Csar.createId(name, version));
}
/**
* @return true if the CSar is a dependency for another or used in a topology.
*/
public boolean isDependency(String csarName, String csarVersion) {
// a csar that is a dependency of another csar
Csar[] result = getDependantCsars(csarName, csarVersion);
if (result != null && result.length > 0) {
return true;
}
// check if some of the nodes are used in topologies.
Topology[] topologies = getDependantTopologies(csarName, csarVersion);
if (topologies != null && topologies.length > 0) {
return true;
}
return false;
}
/**
* Delete an archive if no topology depends from it.
*
* @param csarId The id of the archive to delete.
*/
public void forceDeleteCsar(String csarId) {
Csar csar = getOrFail(csarId);
deleteCsar(csar);
}
/**
* Delete an archive if no topology depends from it.
*
* @param csarId The id of the archive to delete.
*/
public void deleteCsar(String csarId) {
Csar csar = getOrFail(csarId);
// a csar that is a dependency of another csar can not be deleted
if (isDependency(csar.getName(), csar.getVersion())) {
throw new DeleteReferencedObjectException("This csar can not be deleted since it's a dependencie for others");
}
deleteCsar(csar);
}
public void deleteCsar(Csar csar) {
// dispatch event before indexing
publisher.publishEvent(new BeforeArchiveDeleted(this, csar.getId()));
deleteCsarContent(csar);
csarDAO.delete(Csar.class, csar.getId());
// physically delete files
alienRepository.removeCSAR(csar.getName(), csar.getVersion());
// dispatch event before indexing
publisher.publishEvent(new AfterArchiveDeleted(this, csar.getId()));
}
/**
* Delete the content of the csar from the repository: elements, topologies
*
* @param csar
*/
public void deleteCsarContent(Csar csar) {
// Delete the topology defined in this archive.
csarDAO.delete(Topology.class, csar.getId());
// latest version indicator will be recomputed to match this new reality
indexerService.deleteElements(csar.getName(), csar.getVersion());
}
/**
* Delete an archive an all its registered / saved elements
* Abort the deletion if the archive is used by some resources
*
* @param csar
* @return A List of {@link Usage} representing the resources using this archive.
*/
public List<Usage> deleteCsarWithElements(Csar csar) {
// if the csar is bound to an application, then do not allow the process
if (Objects.equals(csar.getDelegateType(), ArchiveDelegateType.APPLICATION.toString())) {
throw new UnsupportedOperationException("Cannot delete an application csar from here ");
}
List<Usage> relatedResourceList = getCsarRelatedResourceList(csar);
if (relatedResourceList.isEmpty()) {
deleteCsar(csar);
}
return relatedResourceList;
}
/**
* Get the list of resources that are using the given archive.
*
* @param csar The archive for which to get usage.
* @return The list of usage of the archive.
*/
public List<Usage> getCsarRelatedResourceList(Csar csar) {
List<Usage> relatedResourceList = Lists.newArrayList();
if (csar == null) {
log.warn("You have requested a resource list for a invalid csar object : <" + csar + ">");
return relatedResourceList;
}
// TODO improve usage infos to add the version of csar/application that uses the given archive.
// a csar that is a dependency of another csar can not be deleted
// FIXME WORKSPACE HANDLING REQUIRED
if (Objects.equals(csar.getDelegateType(), ArchiveDelegateType.APPLICATION.toString())) {
// The CSAR is from an application's topology
relatedResourceList.addAll(
Collections.singletonList(new Usage(csar.getDelegateId(), Application.class.getSimpleName().toLowerCase(), csar.getDelegateId(), null)));
}
Csar[] relatedCsars = getDependantCsars(csar.getName(), csar.getVersion());
if (relatedCsars != null && relatedCsars.length > 0) {
relatedResourceList.addAll(generateCsarsInfo(relatedCsars));
}
// check if some of the nodes are used in topologies.
Topology[] topologies = getDependantTopologies(csar.getName(), csar.getVersion());
if (topologies != null && topologies.length > 0) {
relatedResourceList.addAll(generateTopologiesInfo(topologies));
}
// a csar that is a dependency of location can not be deleted
Location[] relatedLocations = getDependantLocations(csar.getName(), csar.getVersion());
if (relatedLocations != null && relatedLocations.length > 0) {
relatedResourceList.addAll(generateLocationsInfo(relatedLocations));
}
return relatedResourceList;
}
/**
* Generate resources related to a csar list
*
* @param csars
* @return
*/
public List<Usage> generateCsarsInfo(Csar[] csars) {
String resourceName;
String resourceId;
List<Usage> resourceList = Lists.newArrayList();
for (Csar csar : csars) {
if (ArchiveDelegateType.APPLICATION.toString().equals(csar.getDelegateType())) {
Application application = applicationService.checkAndGetApplication(csar.getDelegateId());
resourceName = application.getName();
resourceId = csar.getDelegateId();
} else {
resourceName = csar.getName();
resourceId = csar.getId();
}
Usage temp = new Usage(resourceName, Csar.class.getSimpleName().toLowerCase(), resourceId, csar.getWorkspace());
resourceList.add(temp);
}
return resourceList;
}
/**
* Generate resources related to a locations list
*
* @param locations
* @return
*/
public List<Usage> generateLocationsInfo(Location[] locations) {
String resourceName;
String resourceId;
List<Usage> resourceList = Lists.newArrayList();
for (Location location : locations) {
resourceName = location.getName();
resourceId = location.getId();
Usage temp = new Usage(resourceName, Location.class.getSimpleName().toLowerCase(), resourceId, AlienConstants.GLOBAL_WORKSPACE_ID);
resourceList.add(temp);
}
return resourceList;
}
/**
* Generate resources (application or template) related to a topology list
*
* @param topologies
* @return
*/
public List<Usage> generateTopologiesInfo(Topology[] topologies) {
List<Usage> resourceList = Lists.newArrayList();
List<Csar> topologiesCsar = getTopologiesCsar(topologies);
for (Csar csar : topologiesCsar) {
if (Objects.equals(csar.getDelegateType(), ArchiveDelegateType.APPLICATION.toString())) {
// get the related application
Application application = applicationService.checkAndGetApplication(csar.getDelegateId());
resourceList.add(new Usage(application.getName(), csar.getDelegateType(), csar.getDelegateId(), csar.getWorkspace()));
} else {
resourceList.add(new Usage(csar.getName() + "[" + csar.getVersion() + "]", "topologyTemplate", csar.getId(), csar.getWorkspace()));
}
}
return resourceList;
}
}