package alien4cloud.deployment; import static alien4cloud.utils.AlienUtils.safe; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.Resource; import javax.inject.Inject; import org.alien4cloud.tosca.model.definitions.PropertyDefinition; import org.alien4cloud.tosca.model.definitions.PropertyValue; import org.alien4cloud.tosca.model.templates.Topology; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.springframework.stereotype.Service; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import alien4cloud.application.ApplicationEnvironmentService; import alien4cloud.model.deployment.DeploymentTopology; import alien4cloud.paas.wf.WorkflowsBuilderService; import alien4cloud.topology.TopologyServiceCore; import alien4cloud.topology.TopologyValidationResult; import alien4cloud.topology.TopologyValidationService; import alien4cloud.topology.task.PropertiesTask; import alien4cloud.topology.task.TaskCode; import alien4cloud.topology.task.TaskLevel; import alien4cloud.topology.validation.LocationPolicyValidationService; import alien4cloud.topology.validation.NodeFilterValidationService; import alien4cloud.topology.validation.TopologyAbstractNodeValidationService; import alien4cloud.topology.validation.TopologyPropertiesValidationService; /** * Perform validation of a topology before deployment. */ @Service public class DeploymentTopologyValidationService { @Resource private TopologyPropertiesValidationService topologyPropertiesValidationService; @Resource private TopologyAbstractNodeValidationService topologyAbstractNodeValidationService; @Resource private WorkflowsBuilderService workflowBuilderService; @Inject private LocationPolicyValidationService locationPolicyValidationService; @Inject private OrchestratorPropertiesValidationService orchestratorPropertiesValidationService; @Inject private DeploymentNodeSubstitutionValidationService substitutionValidationServices; @Inject private NodeFilterValidationService nodeFilterValidationService; @Inject private DeploymentInputArtifactValidationService deploymentInputArtifactValidationService; @Inject private InputsPreProcessorService inputsPreProcessorService; @Inject private ApplicationEnvironmentService environmentService; @Inject private TopologyServiceCore topologyServiceCore; /** * Perform validation of a deployment topology. * * @param deploymentTopology The topology to check. * @return A DeploymentTopologyValidationResult with a list of errors and/or warnings er steps. */ public TopologyValidationResult validateDeploymentTopology(DeploymentTopology deploymentTopology) { Topology initialTopology = topologyServiceCore.getOrFail(deploymentTopology.getInitialTopologyId()); // Before validation we inject the inputs into properties to ease validation. This is reverted after. Map<String, PropertyValue> inputs = inputsPreProcessorService.injectInputValues(deploymentTopology, environmentService.getOrFail(deploymentTopology.getEnvironmentId()), initialTopology); TopologyValidationResult result = validateProcessedDeploymentTopology(deploymentTopology, inputs); // And after we revert the injection inputsPreProcessorService.cleanInputValues(deploymentTopology, initialTopology); return result; } /** * Perform validation of a deployment topology where all inputs have been processed. * * @param deploymentTopology The deployment topology to check. * @return A DeploymentTopologyValidationResult with a list of errors and/or warnings er steps. */ public TopologyValidationResult validateProcessedDeploymentTopology(DeploymentTopology deploymentTopology, Map<String, PropertyValue> inputs) { TopologyValidationResult dto = new TopologyValidationResult(); if (deploymentTopology.getNodeTemplates() == null || deploymentTopology.getNodeTemplates().size() < 1) { dto.setValid(false); return dto; } // TODO Perform validation of policies // If a policy is not matched on the location this is a warning as we allow deployment but some features may be missing // If a policy requires a configuration or cannot be applied du to any reason the policy implementation itself can trigger some errors (see Orchestrator // plugins) // validate workflows dto.addTasks(workflowBuilderService.validateWorkflows(deploymentTopology)); // validate abstract node types dto.addTasks(topologyAbstractNodeValidationService.findReplacementForAbstracts(deploymentTopology)); // validate substitutions dto.addTasks(substitutionValidationServices.validateNodeSubstitutions(deploymentTopology)); // location policies dto.addTasks(locationPolicyValidationService.validateLocationPolicies(deploymentTopology)); // validate inputs properties dto.addTask(validateInputProperties(deploymentTopology, inputs)); dto.addTasks(deploymentInputArtifactValidationService.validate(deploymentTopology)); // validate required properties (properties of NodeTemplate, Relationship and Capability) // check also location / ENVIRONMENT meta properties List<PropertiesTask> validateProperties = topologyPropertiesValidationService.validateAllProperties(deploymentTopology); // validate orchestrator properties PropertiesTask orchestratorValidation = orchestratorPropertiesValidationService.validate(deploymentTopology); if (orchestratorValidation != null) { dto.addTasks(Lists.newArrayList(orchestratorValidation)); } // Validate node filters requirements dto.addTasks(nodeFilterValidationService.validateAllRequirementFilters(deploymentTopology)); if (TopologyValidationService.hasOnlyPropertiesWarnings(validateProperties)) { dto.addWarnings(validateProperties); } else { dto.addTasks(validateProperties); } dto.setValid(TopologyValidationService.isValidTaskList(dto.getTaskList())); return dto; } /** * Validate all required input is provided with a non null value * * @param deploymentTopology The deployment topology to check. * @param inputs The map that contains all topology inputs including the one from location and/or application meta properties and tags. * @return A property task with all required missing values or null if all required properties are configured. */ private PropertiesTask validateInputProperties(DeploymentTopology deploymentTopology, Map<String, PropertyValue> inputs) { if (MapUtils.isEmpty(inputs)) { return null; } // Define a task regarding properties PropertiesTask task = new PropertiesTask(); task.setCode(TaskCode.INPUT_PROPERTY); task.setProperties(Maps.<TaskLevel, List<String>> newHashMap()); task.getProperties().put(TaskLevel.REQUIRED, Lists.<String> newArrayList()); Map<String, PropertyValue> inputValues = safe(inputs); for (Entry<String, PropertyDefinition> propDef : safe(deploymentTopology.getInputs().entrySet())) { if (propDef.getValue().isRequired() && inputValues.get(propDef.getKey()) == null) { task.getProperties().get(TaskLevel.REQUIRED).add(propDef.getKey()); } } return CollectionUtils.isNotEmpty(task.getProperties().get(TaskLevel.REQUIRED)) ? task : null; } }