Concourse: reconciler checks for duplicate resource names

This commit is contained in:
Kris De Volder
2016-12-23 18:57:39 -08:00
parent fe449517ea
commit 4ea3e8371f
5 changed files with 118 additions and 5 deletions

View File

@@ -27,11 +27,15 @@ import org.springframework.ide.vscode.commons.yaml.ast.YamlASTProvider;
import org.springframework.ide.vscode.commons.yaml.ast.YamlFileAST;
import org.springframework.ide.vscode.commons.yaml.ast.YamlParser;
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
import org.springframework.ide.vscode.concourse.util.CollectorUtil;
import org.springframework.ide.vscode.concourse.util.StaleFallbackCache;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.Node;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multisets;
/**
* ConcourseModels is responsible for extracting various bits of information
* out of .yml documents and caching them for use by various tools (reconcile engine
@@ -76,7 +80,7 @@ public class ConcourseModel {
* names (e.g. because there hasn't been a successful parse yet and current document contents
* can not be parsed).
*/
public Set<String> getResourceNames(IDocument doc) {
public Multiset<String> getResourceNames(IDocument doc) {
return getStringsFromAst(doc, RESOURCE_NAMES_PATH);
}
@@ -90,18 +94,18 @@ public class ConcourseModel {
* names (e.g. because there hasn't been a successful parse yet and current document contents
* can not be parsed).
*/
public Set<String> getJobNames(IDocument doc) {
public Multiset<String> getJobNames(IDocument doc) {
return getStringsFromAst(doc, JOB_NAMES_PATH);
}
private Set<String> getStringsFromAst(IDocument doc, YamlPath path) {
private Multiset<String> getStringsFromAst(IDocument doc, YamlPath path) {
return getFromAst(doc, (ast) -> {
Node root = ast.get(0);
return path
.traverseAmbiguously(root)
.map(NodeUtil::asScalar)
.filter((string) -> string!=null)
.collect(Collectors.toSet());
.collect(CollectorUtil.toMultiset());
});
}

View File

@@ -106,6 +106,9 @@ public class PipelineYmlSchema implements YamlSchema {
return models.getJobNames(dc.getDocument());
}
);
YAtomicType resourceNameDef = f.yatomic("Resource Name");
resourceNameDef.parseWith(ValueParsers.resourceNameDef(models));
YBeanType getStep = f.ybean("GetStep");
prop(getStep, "get", resourceName);
@@ -157,7 +160,7 @@ public class PipelineYmlSchema implements YamlSchema {
prop(step, "timeout", t_duration);
YBeanType resource = f.ybean("Resource");
prop(resource, "name", t_ne_string);
prop(resource, "name", resourceNameDef);
prop(resource, "type", t_resource_type_name);
prop(resource, "source", t_any);

View File

@@ -14,6 +14,9 @@ import org.springframework.ide.vscode.commons.util.Assert;
import org.springframework.ide.vscode.commons.util.RegexpParser;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.ValueParser;
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
import com.google.common.collect.Multiset;
/**
* Methods and constants to create/get parsers for some atomic types
@@ -33,6 +36,19 @@ public class ValueParsers {
public static final ValueParser POS_INTEGER = integerRange(0, null);
public static final SchemaContextAware<ValueParser> resourceNameDef(ConcourseModel models) {
return (dc) -> {
Multiset<String> resourceNames = models.getResourceNames(dc.getDocument());
return (String input) -> {
if (resourceNames.count(input)<=1) {
//okay
return resourceNames;
}
throw new IllegalArgumentException("Duplicate resource name '"+input+"'");
};
};
};
public static ValueParser integerAtLeast(final Integer lowerBound) {
return integerRange(lowerBound, null);
}

View File

@@ -0,0 +1,67 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.concourse.util;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
/**
* Stuff missing from {@link Collectors} that we implement ourself.
*/
public class CollectorUtil {
/**
* Collects elements into a ImmutableMutiset (the set is converted to an immutable one
* at the end. Accumulating / combining is done with a mutable Multiset because that involves
* less copying.)
*/
public static <T> Collector<T, HashMultiset<T>, ImmutableMultiset<T>> toMultiset() {
return new Collector<T, HashMultiset<T>, ImmutableMultiset<T>>() {
@Override
public Supplier<HashMultiset<T>> supplier() {
return HashMultiset::create;
}
@Override
public BiConsumer<HashMultiset<T>, T> accumulator() {
return (a, e) -> a.add(e);
}
@Override
public BinaryOperator<HashMultiset<T>> combiner() {
return (a1, a2) -> {
a1.addAll(a2);
return a1;
};
}
@Override
public Function<HashMultiset<T>, ImmutableMultiset<T>> finisher() {
return ImmutableMultiset::copyOf;
}
@Override
public Set<Collector.Characteristics> characteristics() {
return ImmutableSet.of(Collector.Characteristics.UNORDERED);
}
};
}
}

View File

@@ -479,6 +479,29 @@ public class PipelineYamlEditorTest {
);
}
@Test
public void reconcileDuplicateResourceNames() throws Exception {
Editor editor = harness.newEditor(
"resources:\n" +
"- name: sts4\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/somestuff\n" +
"- name: utils\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/someutils\n" +
"- name: sts4\n" +
" type: git\n" +
" source:\n" +
" repository: https://github.com/kdvolder/extras\n"
);
editor.assertProblems(
"sts4|Duplicate resource name",
"sts4|Duplicate resource name"
);
}
@Test
public void completionsResourceReferences() throws Exception {
assertContextualCompletions(