package alien4cloud.tosca.parser.postprocess; import static alien4cloud.utils.AlienUtils.safe; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import org.alien4cloud.tosca.model.CSARDependency; import org.alien4cloud.tosca.model.Csar; import org.alien4cloud.tosca.model.templates.NodeGroup; import org.alien4cloud.tosca.model.templates.NodeTemplate; import org.alien4cloud.tosca.model.templates.Topology; import org.alien4cloud.tosca.model.types.AbstractToscaType; import org.springframework.stereotype.Component; import org.yaml.snakeyaml.nodes.Node; import com.google.common.collect.Sets; import alien4cloud.paas.wf.AbstractActivity; import alien4cloud.paas.wf.AbstractStep; import alien4cloud.paas.wf.NodeActivityStep; import alien4cloud.paas.wf.Workflow; import alien4cloud.paas.wf.WorkflowsBuilderService; import alien4cloud.paas.wf.util.WorkflowUtils; import alien4cloud.tosca.context.ToscaContext; import alien4cloud.tosca.model.ArchiveRoot; import alien4cloud.tosca.parser.ParsingContextExecution; import alien4cloud.tosca.parser.ParsingError; import alien4cloud.tosca.parser.ParsingErrorLevel; import alien4cloud.tosca.parser.impl.ErrorCode; /** * Post process a topology. */ @Component public class TopologyPostProcessor implements IPostProcessor<Topology> { @Resource private NodeTemplatePostProcessor nodeTemplatePostProcessor; @Resource private NodeTemplateRelationshipPostProcessor nodeTemplateRelationshipPostProcessor; @Resource private SubstitutionMappingPostProcessor substitutionMappingPostProcessor; @Resource private GroupPostProcessor groupPostProcessor; @Resource private WorkflowsBuilderService workflowBuilderService; @Resource private PropertyDefinitionPostProcessor propertyDefinitionPostProcessor; // Inputs do not define artifact reference so we don't perform validation on them like for types. @Resource private TypeDeploymentArtifactPostProcessor typeDeploymentArtifactPostProcessor; @Override public void process(Topology instance) { if (instance == null) { return; } ArchiveRoot archiveRoot = ParsingContextExecution.getRootObj(); Node node = ParsingContextExecution.getObjectToNodeMap().get(instance); // The yaml node for the topology setDependencies(instance, archiveRoot); if (instance.isEmpty()) { // if the topology doesn't contains any node template it won't be imported so add a warning. ParsingContextExecution.getParsingErrors() .add(new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.EMPTY_TOPOLOGY, null, node.getStartMark(), null, node.getEndMark(), "")); } // Inputs validation safe(instance.getInputs()).entrySet().stream().forEach(propertyDefinitionPostProcessor); safe(instance.getInputArtifacts()).values().stream().forEach(typeDeploymentArtifactPostProcessor); int groupIndex = 0; // Groups validation for (NodeGroup nodeGroup : safe(instance.getGroups()).values()) { nodeGroup.setIndex(groupIndex++); groupPostProcessor.process(nodeGroup); } // Node templates validation for (Map.Entry<String, NodeTemplate> nodeTemplateEntry : safe(instance.getNodeTemplates()).entrySet()) { nodeTemplateEntry.getValue().setName(nodeTemplateEntry.getKey()); nodeTemplatePostProcessor.process(nodeTemplateEntry.getValue()); } safe(instance.getNodeTemplates()).values().stream().forEach(nodeTemplateRelationshipPostProcessor); substitutionMappingPostProcessor.process(instance.getSubstitutionMapping()); // Workflow validation if any are defined WorkflowsBuilderService.TopologyContext topologyContext = workflowBuilderService .buildCachedTopologyContext(new WorkflowsBuilderService.TopologyContext() { @Override public Topology getTopology() { return instance; } @Override public <T extends AbstractToscaType> T findElement(Class<T> clazz, String id) { return ToscaContext.get(clazz, id); } }); finalizeParsedWorkflows(topologyContext, node); } private void setDependencies(Topology instance, ArchiveRoot archiveRoot) { if (archiveRoot.getArchive().getDependencies() == null) { return; } instance.setDependencies(Sets.newHashSet(archiveRoot.getArchive().getDependencies())); } /** * Called after yaml parsing. */ private void finalizeParsedWorkflows(WorkflowsBuilderService.TopologyContext topologyContext, Node node) { if (topologyContext.getTopology().getWorkflows() == null || topologyContext.getTopology().getWorkflows().isEmpty()) { return; } for (Workflow wf : topologyContext.getTopology().getWorkflows().values()) { wf.setStandard(WorkflowUtils.isStandardWorkflow(wf)); if (wf.getSteps() != null) { for (AbstractStep step : wf.getSteps().values()) { if (step.getFollowingSteps() != null) { Iterator<String> followingIds = step.getFollowingSteps().iterator(); while (followingIds.hasNext()) { String followingId = followingIds.next(); AbstractStep followingStep = wf.getSteps().get(followingId); if (followingStep == null) { followingIds.remove(); ParsingContextExecution.getParsingErrors().add(new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.UNKNWON_WORKFLOW_STEP, null, node.getStartMark(), null, node.getEndMark(), followingId)); } else { followingStep.addPreceding(step.getName()); } } } if (step instanceof NodeActivityStep) { AbstractActivity activity = ((NodeActivityStep) step).getActivity(); if (activity == null) { // add an error ? } else { activity.setNodeId(((NodeActivityStep) step).getNodeId()); } } } } WorkflowUtils.fillHostId(wf, topologyContext); int errorCount = workflowBuilderService.validateWorkflow(topologyContext, wf); if (errorCount > 0) { ParsingContextExecution.getParsingErrors().add(new ParsingError(ParsingErrorLevel.WARNING, ErrorCode.WORKFLOW_HAS_ERRORS, null, node.getStartMark(), null, node.getEndMark(), wf.getName())); } } } }