package alien4cloud.tosca.parser.postprocess; import static alien4cloud.utils.AlienUtils.safe; import java.util.HashMap; import java.util.Map; import java.util.Objects; import javax.annotation.Resource; import org.alien4cloud.tosca.model.definitions.AbstractPropertyValue; import org.alien4cloud.tosca.model.definitions.DeploymentArtifact; import org.alien4cloud.tosca.model.definitions.Interface; import org.alien4cloud.tosca.model.definitions.Operation; import org.alien4cloud.tosca.model.definitions.RequirementDefinition; import org.alien4cloud.tosca.model.templates.Capability; import org.alien4cloud.tosca.model.templates.NodeTemplate; import org.alien4cloud.tosca.model.templates.RelationshipTemplate; import org.alien4cloud.tosca.model.types.NodeType; import org.alien4cloud.tosca.model.types.RelationshipType; import org.springframework.stereotype.Component; import org.yaml.snakeyaml.nodes.Node; import com.google.common.collect.Maps; import alien4cloud.tosca.context.ToscaContext; import alien4cloud.tosca.model.ArchiveRoot; import alien4cloud.tosca.parser.ParsingContextExecution; import alien4cloud.tosca.parser.ParsingError; import alien4cloud.tosca.parser.impl.ErrorCode; import alien4cloud.tosca.topology.NodeTemplateBuilder; @Component public class RelationshipPostProcessor { @Resource private ReferencePostProcessor referencePostProcessor; @Resource private PropertyValueChecker propertyValueChecker; @Resource private TemplateDeploymentArtifactPostProcessor templateDeploymentArtifactPostProcessor; @Resource private ImplementationArtifactPostProcessor implementationArtifactPostProcessor; public void process(NodeType nodeTemplateType, Map.Entry<String, RelationshipTemplate> instance) { RelationshipTemplate relationshipTemplate = instance.getValue(); if (relationshipTemplate.getTarget() == null) { Node node = ParsingContextExecution.getObjectToNodeMap().get(instance); // the node template name is required ParsingContextExecution.getParsingErrors() .add(new ParsingError(ErrorCode.REQUIREMENT_TARGET_NODE_TEMPLATE_NAME_REQUIRED, null, node.getStartMark(), null, node.getEndMark(), null)); } RelationshipType relationshipType = ToscaContext.get(RelationshipType.class, relationshipTemplate.getType()); propertyValueChecker.checkProperties(relationshipType, relationshipTemplate.getProperties(), instance.getKey()); RequirementDefinition rd = getRequirementDefinitionByName(nodeTemplateType, relationshipTemplate.getRequirementName()); if (rd == null) { Node node = ParsingContextExecution.getObjectToNodeMap().get(relationshipTemplate.getRequirementName()); ParsingContextExecution.getParsingErrors().add(new ParsingError(ErrorCode.REQUIREMENT_NOT_FOUND, null, node.getStartMark(), null, node.getEndMark(), relationshipTemplate.getRequirementName())); return; } if (relationshipTemplate.getType() == null) { // if the relationship type has not been defined on the requirement assignment it may be defined on the requirement definition. relationshipTemplate.setType(rd.getRelationshipType()); } referencePostProcessor.process(new ReferencePostProcessor.TypeReference(relationshipTemplate, relationshipTemplate.getType(), RelationshipType.class)); relationshipTemplate.setRequirementType(rd.getType()); ArchiveRoot archiveRoot = (ArchiveRoot) ParsingContextExecution.getRoot().getWrappedInstance(); // now find the target of the relation NodeTemplate targetNodeTemplate = archiveRoot.getTopology().getNodeTemplates().get(relationshipTemplate.getTarget()); if (targetNodeTemplate == null) { Node node = ParsingContextExecution.getObjectToNodeMap().get(relationshipTemplate.getTarget()); ParsingContextExecution.getParsingErrors().add(new ParsingError(ErrorCode.REQUIREMENT_TARGET_NOT_FOUND, null, node.getStartMark(), null, node.getEndMark(), relationshipTemplate.getTarget())); return; } String capabilityType = relationshipTemplate.getTargetedCapabilityName(); // alien actually supports a capability type in the TOSCA yaml Capability capability = null; if (capabilityType == null) { // the capability type is not known, we assume that we are parsing a Short notation (node only) // in such notation : "a requirement named ‘host’ that needs to be fulfilled by the same named capability" // so here we use the requirement name to find the capability if (targetNodeTemplate.getCapabilities() != null) { capability = targetNodeTemplate.getCapabilities().get(relationshipTemplate.getRequirementName()); if (capability != null) { relationshipTemplate.setTargetedCapabilityName(rd.getId()); } } } else { Map.Entry<String, Capability> capabilityEntry = getCapabilityByType(targetNodeTemplate, capabilityType); if (capabilityEntry != null) { capability = capabilityEntry.getValue(); relationshipTemplate.setTargetedCapabilityName(capabilityEntry.getKey()); } } if (capability == null) { Node node = ParsingContextExecution.getObjectToNodeMap().get(relationshipTemplate); // we should fail ParsingContextExecution.getParsingErrors().add(new ParsingError(ErrorCode.REQUIREMENT_CAPABILITY_NOT_FOUND, null, node.getStartMark(), null, node.getEndMark(), relationshipTemplate.getRequirementName())); return; } RelationshipType indexedRelationshipType = ToscaContext.get(RelationshipType.class, relationshipTemplate.getType()); if (indexedRelationshipType == null) { // Error managed by the reference post processor. return; } Map<String, AbstractPropertyValue> properties = Maps.newLinkedHashMap(); NodeTemplateBuilder.fillProperties(properties, indexedRelationshipType.getProperties(), relationshipTemplate.getProperties(), false); relationshipTemplate.setProperties(properties); relationshipTemplate.setAttributes(indexedRelationshipType.getAttributes()); // FIXME we should check that the artifact is defined at the type level. safe(instance.getValue().getArtifacts()).values().forEach(templateDeploymentArtifactPostProcessor); Map<String, DeploymentArtifact> mergedArtifacts = instance.getValue().getArtifacts(); if (mergedArtifacts == null) { mergedArtifacts = new HashMap<>(); } mergedArtifacts.putAll(safe(indexedRelationshipType.getArtifacts())); relationshipTemplate.setArtifacts(mergedArtifacts); // TODO Manage interfaces inputs to copy them to all operations. for (Interface anInterface : safe(instance.getValue().getInterfaces()).values()) { safe(anInterface.getOperations()).values().stream().map(Operation::getImplementationArtifact).filter(Objects::nonNull) .forEach(implementationArtifactPostProcessor); } } private Map.Entry<String, Capability> getCapabilityByType(NodeTemplate nodeTemplate, String type) { if (nodeTemplate.getCapabilities() == null) { // add a check in case the node doesn't have capabilities return null; } for (Map.Entry<String, Capability> capabilityEntry : nodeTemplate.getCapabilities().entrySet()) { if (type.equals(capabilityEntry.getValue().getType())) { return capabilityEntry; } } return null; } private RequirementDefinition getRequirementDefinitionByName(NodeType indexedNodeType, String name) { if (indexedNodeType.getRequirements() != null) { for (RequirementDefinition rd : indexedNodeType.getRequirements()) { if (rd.getId().equals(name)) { return rd; } } } return null; } }