com.thoughtworks.go.config.PipelineConfigTreeValidator.java Source code

Java tutorial

Introduction

Here is the source code for com.thoughtworks.go.config.PipelineConfigTreeValidator.java

Source

/*
 * Copyright 2019 ThoughtWorks, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thoughtworks.go.config;

import com.rits.cloning.Cloner;
import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig;
import com.thoughtworks.go.domain.ConfigErrors;
import com.thoughtworks.go.domain.Task;
import com.thoughtworks.go.domain.materials.MaterialConfig;
import com.thoughtworks.go.util.*;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;

public class PipelineConfigTreeValidator {
    private final PipelineConfig pipelineConfig;

    public PipelineConfigTreeValidator(PipelineConfig pipelineConfig) {
        this.pipelineConfig = pipelineConfig;
    }

    public boolean validate(PipelineConfigSaveValidationContext validationContext) {
        pipelineConfig.validate(validationContext);
        pipelineCreationSpecificValidations(validationContext);

        validateDependencies(validationContext);
        boolean isValid = pipelineConfig.errors().isEmpty();
        PipelineConfigSaveValidationContext contextForChildren = validationContext.withParent(pipelineConfig);

        for (StageConfig stageConfig : pipelineConfig.getStages()) {
            isValid = stageConfig.validateTree(contextForChildren) && isValid;
            if (pipelineConfig.hasTemplateApplied()) {
                final List<ConfigErrors> allErrors = new ArrayList<>();
                new GoConfigGraphWalker(stageConfig).walk(new ErrorCollectingHandler(allErrors) {
                    @Override
                    public void handleValidation(Validatable validatable, ValidationContext context) {
                    }
                });
                for (ConfigErrors error : allErrors) {
                    pipelineConfig.errors().add("template", StringUtils.join(error.getAll(), ", "));
                }
            }
        }
        validateCyclicDependencies(validationContext);
        isValid = pipelineConfig.materialConfigs().validateTree(contextForChildren) && isValid;
        isValid = pipelineConfig.getParams().validateTree(contextForChildren) && isValid;
        isValid = pipelineConfig.getVariables().validateTree(contextForChildren) && isValid;
        if (pipelineConfig.getTrackingTool() != null)
            isValid = pipelineConfig.getTrackingTool().validateTree(contextForChildren) && isValid;
        if (pipelineConfig.getMingleConfig() != null)
            isValid = pipelineConfig.getMingleConfig().validateTree(contextForChildren) && isValid;
        if (pipelineConfig.getTimer() != null)
            isValid = pipelineConfig.getTimer().validateTree(contextForChildren) && isValid;
        return isValid;
    }

    private void pipelineCreationSpecificValidations(PipelineConfigSaveValidationContext validationContext) {
        if (validationContext.isPipelineBeingCreated()) {
            validationContext.getGroups().validatePipelineNameUniqueness();
            PipelineConfigs group = validationContext.getPipelineGroup();
            group.validateGroupNameAndAddErrorsTo(pipelineConfig.errors());
        }
    }

    private void validateCyclicDependencies(PipelineConfigSaveValidationContext validationContext) {
        final DFSCycleDetector dfsCycleDetector = new DFSCycleDetector();
        try {
            dfsCycleDetector.topoSort(pipelineConfig.name(),
                    new PipelineConfigValidationContextDependencyState(pipelineConfig, validationContext));
        } catch (Exception e) {
            pipelineConfig.materialConfigs().addError("base", e.getMessage());
        }
    }

    void validateDependencies(PipelineConfigSaveValidationContext validationContext) {
        if (validationContext.isPipelineBeingCreated())
            return;
        for (CaseInsensitiveString selected : validationContext.getPipelinesWithDependencyMaterials()) {
            if (selected.equals(pipelineConfig.name()))
                continue;
            PipelineConfig selectedPipeline = validationContext.getPipelineConfigByName(selected);
            validateDependencyMaterialsForDownstreams(validationContext, selected, selectedPipeline);
            validateFetchTasksForOtherPipelines(validationContext, selectedPipeline);
        }
    }

    private void validateDependencyMaterialsForDownstreams(PipelineConfigSaveValidationContext validationContext,
            CaseInsensitiveString selected, PipelineConfig downstreamPipeline) {
        Node dependenciesOfSelectedPipeline = validationContext.getDependencyMaterialsFor(selected);
        for (Node.DependencyNode dependencyNode : dependenciesOfSelectedPipeline.getDependencies()) {
            if (dependencyNode.getPipelineName().equals(pipelineConfig.name())) {
                for (MaterialConfig materialConfig : downstreamPipeline.materialConfigs()) {
                    if (materialConfig instanceof DependencyMaterialConfig) {
                        DependencyMaterialConfig dependencyMaterialConfig = new Cloner()
                                .deepClone((DependencyMaterialConfig) materialConfig);
                        dependencyMaterialConfig.validate(validationContext.withParent(downstreamPipeline));
                        List<String> allErrors = dependencyMaterialConfig.errors().getAll();
                        for (String error : allErrors) {
                            pipelineConfig.errors().add("base", error);
                        }
                    }
                }
            }
        }
    }

    private void validateFetchTasksForOtherPipelines(PipelineConfigSaveValidationContext validationContext,
            PipelineConfig downstreamPipeline) {
        for (StageConfig stageConfig : downstreamPipeline.getStages()) {
            for (JobConfig jobConfig : stageConfig.getJobs()) {
                for (Task task : jobConfig.getTasks()) {
                    if (task instanceof FetchTask) {
                        FetchTask fetchTask = (FetchTask) task;
                        if (fetchTask.getPipelineNamePathFromAncestor() != null
                                && !StringUtils.isBlank(CaseInsensitiveString
                                        .str(fetchTask.getPipelineNamePathFromAncestor().getPath()))
                                && fetchTask.getPipelineNamePathFromAncestor().pathIncludingAncestor()
                                        .contains(pipelineConfig.name())) {
                            fetchTask = new Cloner().deepClone(fetchTask);
                            fetchTask.validateTask(validationContext.withParent(downstreamPipeline)
                                    .withParent(stageConfig).withParent(jobConfig));
                            List<String> allErrors = fetchTask.errors().getAll();
                            for (String error : allErrors) {
                                pipelineConfig.errors().add("base", error);
                            }
                        }
                    }
                }
            }
        }
    }

    private class PipelineConfigValidationContextDependencyState implements PipelineDependencyState {
        private PipelineConfig pipelineConfig;
        private PipelineConfigSaveValidationContext validationContext;

        public PipelineConfigValidationContextDependencyState(PipelineConfig pipelineConfig,
                PipelineConfigSaveValidationContext validationContext) {
            this.pipelineConfig = pipelineConfig;
            this.validationContext = validationContext;
        }

        @Override
        public boolean hasPipeline(CaseInsensitiveString key) {
            return validationContext.getPipelineConfigByName(key) != null;
        }

        @Override
        public Node getDependencyMaterials(CaseInsensitiveString pipelineName) {
            if (pipelineConfig.name().equals(pipelineName))
                return pipelineConfig.getDependenciesAsNode();
            return validationContext.getDependencyMaterialsFor(pipelineName);
        }
    }

}