/* * Copyright 2015 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.materials; import java.util.*; import com.thoughtworks.go.config.*; import com.thoughtworks.go.config.materials.dependency.DependencyMaterialConfig; import com.thoughtworks.go.config.materials.git.GitMaterialConfig; import com.thoughtworks.go.config.materials.mercurial.HgMaterialConfig; import com.thoughtworks.go.config.materials.perforce.P4MaterialConfig; import com.thoughtworks.go.config.materials.svn.SvnMaterialConfig; import com.thoughtworks.go.config.materials.tfs.TfsMaterialConfig; import com.thoughtworks.go.config.remote.ConfigOrigin; import com.thoughtworks.go.domain.BaseCollection; import com.thoughtworks.go.domain.ConfigErrors; import com.thoughtworks.go.domain.materials.MaterialConfig; import com.thoughtworks.go.security.GoCipher; import com.thoughtworks.go.util.ArtifactLogUtil; import com.thoughtworks.go.util.StringUtil; import com.thoughtworks.go.util.command.UrlArgument; import org.apache.commons.lang.StringUtils; @ConfigTag("materials") @ConfigCollection(MaterialConfig.class) public class MaterialConfigs extends BaseCollection<MaterialConfig> implements Validatable, ParamsAttributeAware { private static final int DEFAULT_INTERVAL = 100; private int intervalInSeconds = DEFAULT_INTERVAL; private ConfigErrors configErrors; public MaterialConfigs() { } public MaterialConfigs(MaterialConfig... materials) { super(materials); } public MaterialConfigs(List<MaterialConfig> materials) { this(DEFAULT_INTERVAL, materials); } public MaterialConfigs(int intervalInSeconds, List<MaterialConfig> materials) { super(materials); this.intervalInSeconds = intervalInSeconds; } public int interval() { return intervalInSeconds; } private List<String> allowedFolders() { ArrayList<String> allowed = new ArrayList<>(); for (MaterialConfig material : this) { if (!StringUtils.isBlank(material.getFolder())) { allowed.add(material.getFolder()); } } allowed.add(ArtifactLogUtil.CRUISE_OUTPUT_FOLDER); return allowed; } boolean hasOneMaterialUseBaseFolder() { for (MaterialConfig material : this) { if (material.getFolder() == null) { return true; } } return false; } public DependencyMaterialConfig findDependencyMaterial(final CaseInsensitiveString upstreamPipeline) { for (MaterialConfig material : this) { if (material instanceof DependencyMaterialConfig) { DependencyMaterialConfig dependencyMaterialConfig = (DependencyMaterialConfig) material; if (upstreamPipeline.equals(dependencyMaterialConfig.getPipelineName())) { return dependencyMaterialConfig; } } } return null; } public List<CaseInsensitiveString> getDependentPipelineNames() { Set<CaseInsensitiveString> names = new TreeSet<>(); for (MaterialConfig material : this) { if (material instanceof DependencyMaterialConfig) { names.add(((DependencyMaterialConfig) material).getPipelineName()); } } return new ArrayList<>(names); } public boolean hasMaterialWithFingerprint(MaterialConfig other) { for (MaterialConfig material : this) { if (material.isSameFlyweight(other)) { return true; } } return false; } public MaterialConfig getByFingerPrint(String fingerPrint) { for (MaterialConfig material : this) { if (material.getPipelineUniqueFingerprint().equals(fingerPrint)) { return material; } } return null; } // TODO: Probably will go away when filter.shouldIgnore is handled. public MaterialConfig get(MaterialConfig other) { for (MaterialConfig material : this) { if (material.isSameFlyweight(other)) { return material; } } throw new RuntimeException("Material not found: " + other);//IMP: because, config can change between BCPS call and build cause production - shilpa/jj } public boolean validateTree(PipelineConfigSaveValidationContext validationContext) { if (isEmpty()){ errors().add("materials", "A pipeline must have at least one material"); } validate(validationContext); boolean isValid = errors().isEmpty(); for (MaterialConfig materialConfig : this) { materialConfig.validateTree(validationContext); isValid = materialConfig.errors().isEmpty() && isValid; } return isValid; } public void validate(ValidationContext validationContext) { validateNameUniqueness(); validateAutoUpdateState(validationContext); validateScmMaterials(); Set<CaseInsensitiveString> dependencies = new HashSet<>(); for (DependencyMaterialConfig material : filterDependencyMaterials()) { material.validateUniqueness(dependencies); } if (validationContext.isWithinPipelines()) { PipelineConfig currentPipeline = validationContext.getPipeline(); validateOrigins(currentPipeline, validationContext); } } private void validateOrigins(PipelineConfig currentPipeline, ValidationContext validationContext) { for (DependencyMaterialConfig material : filterDependencyMaterials()) { PipelineConfig upstream = validationContext.getPipelineConfigByName(material.getPipelineName()); if (upstream == null) continue; // other rule validates existence of upstream ConfigOrigin myOrigin = currentPipeline.getOrigin(); ConfigOrigin upstreamOrigin = upstream.getOrigin(); if (validationContext.shouldCheckConfigRepo()) { if (!validationContext.getConfigRepos().isReferenceAllowed(myOrigin, upstreamOrigin)) { material.addError(DependencyMaterialConfig.ORIGIN, String.format("Dependency from pipeline defined in %s to pipeline defined in %s is not allowed", displayNameFor(myOrigin), displayNameFor(upstreamOrigin))); } } } } private String displayNameFor(ConfigOrigin origin) { return origin != null ? origin.displayName() : "cruise-config.xml"; } private void validateScmMaterials() { List<MaterialConfig> allSCMMaterials = getSCMAndPluggableSCMConfigs(); if (allSCMMaterials.size() > 1) { for (MaterialConfig material : allSCMMaterials) { if (StringUtil.isBlank(material.getFolder())) { String fieldName = material instanceof ScmMaterialConfig ? ScmMaterialConfig.FOLDER : PluggableSCMMaterialConfig.FOLDER; material.addError(fieldName, "Destination directory is required when specifying multiple scm materials"); } else { validateDestinationFolder(allSCMMaterials, material); } } } } private List<MaterialConfig> getSCMAndPluggableSCMConfigs() { List<ScmMaterialConfig> scmMaterials = filterScmMaterials(); List<PluggableSCMMaterialConfig> pluggableSCMMaterials = filterPluggableSCMMaterials(); List<MaterialConfig> allSCMMaterials = new ArrayList<>(); allSCMMaterials.addAll(scmMaterials); allSCMMaterials.addAll(pluggableSCMMaterials); return allSCMMaterials; } private void validateDestinationFolder(List<MaterialConfig> allSCMMaterials, MaterialConfig material) { String materialFolder = material.getFolder(); for (MaterialConfig otherMaterial : allSCMMaterials) { if (otherMaterial != material) { if (otherMaterial instanceof ScmMaterialConfig) { ((ScmMaterialConfig) otherMaterial).validateNotSubdirectoryOf(materialFolder); ((ScmMaterialConfig) otherMaterial).validateDestinationDirectoryName(materialFolder); } else { ((PluggableSCMMaterialConfig) otherMaterial).validateNotSubdirectoryOf(materialFolder); ((PluggableSCMMaterialConfig) otherMaterial).validateDestinationDirectoryName(materialFolder); } } } } /* To two methods below are to avoid creating methods on already long Material interface with a No Op implementations. */ private List<ScmMaterialConfig> filterScmMaterials() { List<ScmMaterialConfig> scmMaterials = new ArrayList<>(); for (MaterialConfig material : this) { if (material instanceof ScmMaterialConfig) { scmMaterials.add((ScmMaterialConfig) material); } } return scmMaterials; } private List<PluggableSCMMaterialConfig> filterPluggableSCMMaterials() { List<PluggableSCMMaterialConfig> pluggableSCMMaterials = new ArrayList<>(); for (MaterialConfig materialConfig : this) { if (materialConfig instanceof PluggableSCMMaterialConfig) { pluggableSCMMaterials.add((PluggableSCMMaterialConfig) materialConfig); } } return pluggableSCMMaterials; } private List<DependencyMaterialConfig> filterDependencyMaterials() { List<DependencyMaterialConfig> dependencyMaterials = new ArrayList<>(); for (MaterialConfig material : this) { if (material instanceof DependencyMaterialConfig) { dependencyMaterials.add((DependencyMaterialConfig) material); } } return dependencyMaterials; } private void validateAutoUpdateState(ValidationContext validationContext) { for (MaterialConfig material : filterScmMaterials()) { String fingerprint; try { fingerprint = material.getFingerprint(); } catch (Exception e) { continue; } MaterialConfigs allMaterialsByFingerPrint = validationContext.getAllMaterialsByFingerPrint(fingerprint); if (allMaterialsByFingerPrint != null && ((ScmMaterialConfig) material).isAutoUpdateStateMismatch(allMaterialsByFingerPrint)) { ((ScmMaterialConfig) material).setAutoUpdateMismatchError(); } } } private void validateNameUniqueness() { Map<CaseInsensitiveString, AbstractMaterialConfig> materialHashMap = new HashMap<>(); for (MaterialConfig material : this) { material.validateNameUniqueness(materialHashMap); } } public ConfigErrors errors() { initErrors(); return configErrors; } public void addError(String fieldName, String message) { initErrors(); configErrors.add(fieldName, message); } private void initErrors() { if (configErrors == null) { configErrors = new ConfigErrors(); } } public SvnMaterialConfig getSvnMaterial() { return getExistingOrDefaultMaterial(new SvnMaterialConfig("", "", "", false)); } public TfsMaterialConfig getTfsMaterial() { return getExistingOrDefaultMaterial(new TfsMaterialConfig(new GoCipher(), new UrlArgument(""), "", "", "", "")); } public HgMaterialConfig getHgMaterial() { return getExistingOrDefaultMaterial(new HgMaterialConfig("", null)); } public GitMaterialConfig getGitMaterial() { return getExistingOrDefaultMaterial(new GitMaterialConfig("")); } public P4MaterialConfig getP4Material() { return getExistingOrDefaultMaterial(new P4MaterialConfig("", "")); } public DependencyMaterialConfig getDependencyMaterial() { return getExistingOrDefaultMaterial(new DependencyMaterialConfig(new CaseInsensitiveString(""), new CaseInsensitiveString(""))); } public PackageMaterialConfig getPackageMaterial() { return getExistingOrDefaultMaterial(new PackageMaterialConfig()); } public PluggableSCMMaterialConfig getSCMMaterial() { return getExistingOrDefaultMaterial(new PluggableSCMMaterialConfig()); } <T extends MaterialConfig> T getExistingOrDefaultMaterial(T defaultMaterial) { for (MaterialConfig material : this) { if (material.getClass().isAssignableFrom(defaultMaterial.getClass())) { return (T) material; } } return defaultMaterial; } public String getMaterialOptions() { return first() == null ? "" : first().getType(); } public void setConfigAttributes(Object attributes) { clear(); if (attributes == null) { return; } Map attributeMap = (Map) attributes; String materialType = (String) attributeMap.get(AbstractMaterialConfig.MATERIAL_TYPE); if (SvnMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getSvnMaterial(), (Map) attributeMap.get(SvnMaterialConfig.TYPE)); } else if (HgMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getHgMaterial(), (Map) attributeMap.get(HgMaterialConfig.TYPE)); } else if (GitMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getGitMaterial(), (Map) attributeMap.get(GitMaterialConfig.TYPE)); } else if (P4MaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getP4Material(), (Map) attributeMap.get(P4MaterialConfig.TYPE)); } else if (DependencyMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getDependencyMaterial(), (Map) attributeMap.get(DependencyMaterialConfig.TYPE)); } else if (TfsMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getTfsMaterial(), (Map) attributeMap.get(TfsMaterialConfig.TYPE)); } else if (PackageMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getPackageMaterial(), (Map) attributeMap.get(PackageMaterialConfig.TYPE)); } else if (PluggableSCMMaterialConfig.TYPE.equals(materialType)) { addMaterialConfig(getSCMMaterial(), (Map) attributeMap.get(PluggableSCMMaterialConfig.TYPE)); } } public boolean scmMaterialsHaveDestination() { for (MaterialConfig scmMaterial : getSCMAndPluggableSCMConfigs()) { String destination = scmMaterial.getFolder(); if (StringUtil.isBlank(destination)) { return false; } } return true; } private void addMaterialConfig(MaterialConfig materialConfig, Map attributes) { materialConfig.setConfigAttributes(attributes); add(materialConfig); } public boolean hasDependencyMaterial(PipelineConfig pipeline) { return findDependencyMaterial(pipeline.name()) != null; } }