package alien4cloud.orchestrators.locations.services; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import javax.inject.Inject; import alien4cloud.component.repository.exception.ToscaTypeAlreadyDefinedInOtherCSAR; import org.alien4cloud.tosca.catalog.index.ArchiveIndexer; import org.alien4cloud.tosca.catalog.index.CsarService; import org.alien4cloud.tosca.model.CSARDependency; import org.alien4cloud.tosca.model.Csar; import org.alien4cloud.tosca.model.types.AbstractToscaType; import org.alien4cloud.tosca.model.types.NodeType; import org.apache.commons.collections4.CollectionUtils; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.springframework.context.ApplicationContext; 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.component.repository.exception.CSARUsedInActiveDeployment; import alien4cloud.dao.IGenericSearchDAO; import alien4cloud.events.LocationArchiveDeleteRequested; import alien4cloud.events.LocationTypeIndexed; import alien4cloud.exception.AlreadyExistException; import alien4cloud.model.common.Tag; import alien4cloud.model.common.Usage; import alien4cloud.model.components.CSARSource; import alien4cloud.model.orchestrators.Orchestrator; import alien4cloud.model.orchestrators.locations.Location; import alien4cloud.orchestrators.plugin.ILocationConfiguratorPlugin; import alien4cloud.orchestrators.plugin.IOrchestratorPlugin; import alien4cloud.orchestrators.plugin.IOrchestratorPluginFactory; import alien4cloud.orchestrators.plugin.model.PluginArchive; import alien4cloud.orchestrators.services.OrchestratorService; import alien4cloud.paas.OrchestratorPluginService; import alien4cloud.paas.exception.OrchestratorDisabledException; import alien4cloud.tosca.model.ArchiveRoot; import alien4cloud.tosca.parser.ParsingError; import lombok.extern.slf4j.Slf4j; /** * Manage the indexing of TOSCA archives. */ @Slf4j @Component @SuppressWarnings("rawtypes") public class PluginArchiveIndexer { @Inject private OrchestratorService orchestratorService; @Inject private OrchestratorPluginService orchestratorPluginService; @Inject private CsarService csarService; @Inject private ArchiveIndexer archiveIndexer; @Resource(name = "alien-es-dao") private IGenericSearchDAO alienDAO; @Inject private ApplicationContext applicationContext; /** * Ensure that location archives are indexed. * * @param orchestrator the orchestrator for which to index archives. * @param location The location of the orchestrator for which to index archives. */ public Set<CSARDependency> indexLocationArchives(Orchestrator orchestrator, Location location) { IOrchestratorPlugin orchestratorInstance = (IOrchestratorPlugin) orchestratorPluginService.getOrFail(orchestrator.getId()); ILocationConfiguratorPlugin configuratorPlugin = orchestratorInstance.getConfigurator(location.getInfrastructureType()); // ensure that the plugin archives for this location are imported in the catalog List<PluginArchive> pluginArchives = configuratorPlugin.pluginArchives(); Set<CSARDependency> dependencies = Sets.newHashSet(); if (pluginArchives == null) { return dependencies; } // index archive here if not already indexed for (PluginArchive pluginArchive : pluginArchives) { ArchiveRoot archive = pluginArchive.getArchive(); Csar csar = csarService.get(archive.getArchive().getName(), archive.getArchive().getVersion()); String lastParsedHash = null; if (csar == null) { // index the required archive indexArchive(pluginArchive, orchestrator, location); lastParsedHash = archive.getArchive().getHash(); } else { lastParsedHash = csar.getHash(); } if (archive.getArchive().getDependencies() != null) { dependencies.addAll(archive.getArchive().getDependencies()); } dependencies.add(new CSARDependency(archive.getArchive().getName(), archive.getArchive().getVersion(), lastParsedHash)); } return dependencies; } /** * Index archives defined at the orchestrator level by a plugin. * * @param orchestratorFactory The orchestrator factory. * @param orchestratorInstance The instance of the orchestrator (created by the factory). */ public void indexOrchestratorArchives(IOrchestratorPluginFactory<IOrchestratorPlugin<?>, ?> orchestratorFactory, IOrchestratorPlugin<Object> orchestratorInstance) { for (PluginArchive pluginArchive : orchestratorInstance.pluginArchives()) { try { archiveIndexer.importArchive(pluginArchive.getArchive(), CSARSource.ORCHESTRATOR, pluginArchive.getArchiveFilePath(), Lists.<ParsingError> newArrayList()); publishLocationTypeIndexedEvent(pluginArchive.getArchive().getNodeTypes().values(), orchestratorFactory, null); } catch (AlreadyExistException e) { log.debug("Skipping orchestrator archive import as the released version already exists in the repository. " + e.getMessage()); } catch (CSARUsedInActiveDeployment e) { log.debug("Skipping orchestrator archive import as it is used in an active deployment. " + e.getMessage()); } catch (ToscaTypeAlreadyDefinedInOtherCSAR e) { log.debug("Skipping orchestrator archive import, it's archive contain's a tosca type already defined in an other archive." + e.getMessage()); } } } private void indexArchive(PluginArchive pluginArchive, Orchestrator orchestrator, Location location) { ArchiveRoot archive = pluginArchive.getArchive(); // inject a specific tag to allow components catalog filtering search injectWorkSpace(archive.getNodeTypes().values(), orchestrator, location); injectWorkSpace(archive.getArtifactTypes().values(), orchestrator, location); injectWorkSpace(archive.getCapabilityTypes().values(), orchestrator, location); injectWorkSpace(archive.getRelationshipTypes().values(), orchestrator, location); List<ParsingError> parsingErrors = Lists.newArrayList(); // index the archive in alien catalog try { archiveIndexer.importArchive(archive, CSARSource.ORCHESTRATOR, pluginArchive.getArchiveFilePath(), parsingErrors); } catch (AlreadyExistException e) { log.debug("Skipping location archive import as the released version already exists in the repository."); } catch (CSARUsedInActiveDeployment e) { log.debug("Skipping orchestrator archive import as it is used in an active deployment. " + e.getMessage()); } catch (ToscaTypeAlreadyDefinedInOtherCSAR e) { log.debug("Skipping orchestrator archive import, it's archive contain's a tosca type already defined in an other archive." + e.getMessage()); } // Publish event to allow plugins to post-process elements (portability plugin for example). publishLocationTypeIndexedEvent(archive.getNodeTypes().values(), orchestrator, location); } private void publishLocationTypeIndexedEvent(Collection<NodeType> collection, Orchestrator orchestrator, Location location) { IOrchestratorPluginFactory orchestratorFactory = orchestratorService.getPluginFactory(orchestrator); publishLocationTypeIndexedEvent(collection, orchestratorFactory, location); } private void publishLocationTypeIndexedEvent(Collection<NodeType> collection, IOrchestratorPluginFactory orchestratorFactory, Location location) { if (CollectionUtils.isNotEmpty(collection)) { for (NodeType nodeType : collection) { LocationTypeIndexed event = new LocationTypeIndexed(this); event.setNodeType(nodeType); event.setLocation(location); event.setOrchestratorFactory(orchestratorFactory); applicationContext.publishEvent(event); } } } private void injectWorkSpace(Collection<? extends AbstractToscaType> elements, Orchestrator orchestrator, Location location) { for (AbstractToscaType element : elements) { if (element.getTags() == null) { element.setTags(new ArrayList<Tag>()); } element.getTags().add(new Tag("alien-workspace-id", orchestrator.getId() + ":" + location.getId())); element.getTags().add(new Tag("alien-workspace-name", orchestrator.getName() + " - " + location.getName())); } } /** * Delete all archives related to a location, if not exposed or used by another location * * @param location * @return Map of usages per archives if found (that means the deletion wasn't performed successfully), null if everything went well. */ public Map<Csar, List<Usage>> deleteArchives(Orchestrator orchestrator, Location location) { ILocationConfiguratorPlugin configuratorPlugin = getConfiguratorPlugin(location); IOrchestratorPluginFactory orchestratorFactory = orchestratorService.getPluginFactory(orchestrator); List<PluginArchive> pluginArchives = configuratorPlugin.pluginArchives(); // abort if no archive is exposed by this location if (CollectionUtils.isEmpty(pluginArchives)) { return null; } Map<String, List<Location>> allExposedArchivesIds = getAllExposedArchivesIdsExluding(location); Map<Csar, List<Usage>> usages = Maps.newHashMap(); for (PluginArchive pluginArchive : pluginArchives) { Csar csar = pluginArchive.getArchive().getArchive(); List<Location> locationsExposingArchive = allExposedArchivesIds.get(csar.getId()); LocationArchiveDeleteRequested e = new LocationArchiveDeleteRequested(this); e.setCsar(csar); e.setLocation(location); e.setOrchestratorFactory(orchestratorFactory); e.setLocationsExposingArchive(locationsExposingArchive); // only delete if no other location exposed this archive if (locationsExposingArchive == null) { List<Usage> csarUsage = csarService.deleteCsarWithElements(csar); if (CollectionUtils.isNotEmpty(csarUsage)) { usages.put(csar, csarUsage); } e.setDeleted(true); } else { e.setDeleted(false); } applicationContext.publishEvent(e); } return usages.isEmpty() ? null : usages; } private ILocationConfiguratorPlugin getConfiguratorPlugin(Location location) { ILocationConfiguratorPlugin configuratorPlugin; try { IOrchestratorPlugin<Object> orchestratorInstance = orchestratorPluginService.getOrFail(location.getOrchestratorId()); configuratorPlugin = orchestratorInstance.getConfigurator(location.getInfrastructureType()); } catch (OrchestratorDisabledException e) { IOrchestratorPluginFactory orchestratorFactory = orchestratorService.getPluginFactory(orchestratorService.getOrFail(location.getOrchestratorId())); IOrchestratorPlugin<Object> orchestratorInstance = orchestratorFactory.newInstance(); configuratorPlugin = orchestratorInstance.getConfigurator(location.getInfrastructureType()); orchestratorFactory.destroy(orchestratorInstance); } return configuratorPlugin; } /** * Query for csars that are defined by locations. * * @return A maps of <csar_id, list of locations that uses the csar>. */ public Map<String, List<Location>> getAllExposedArchivesIdsExluding(Location excludedLocation) { // exclude a location from the search QueryBuilder query = QueryBuilders.boolQuery() .mustNot(QueryBuilders.idsQuery(Location.class.getSimpleName().toLowerCase()).ids(excludedLocation.getId())); List<Location> locations = alienDAO.customFindAll(Location.class, query); Map<String, List<Location>> archiveIds = Maps.newHashMap(); if (locations != null) { for (Location location : locations) { ILocationConfiguratorPlugin configuratorPlugin = getConfiguratorPlugin(location); List<PluginArchive> pluginArchives = configuratorPlugin.pluginArchives(); for (PluginArchive pluginArchive : pluginArchives) { String archiveId = pluginArchive.getArchive().getArchive().getId(); List<Location> locationsPerArchive = archiveIds.get(archiveId); if (locationsPerArchive == null) { locationsPerArchive = Lists.newArrayList(); archiveIds.put(archiveId, locationsPerArchive); } locationsPerArchive.add(location); } } } return archiveIds; } }