diff options
Diffstat (limited to 'config-model')
4 files changed, 123 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/ReservedDocumentTypeNameValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/content/ReservedDocumentTypeNameValidator.java new file mode 100644 index 00000000000..9f081f7083b --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/content/ReservedDocumentTypeNameValidator.java @@ -0,0 +1,42 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.content; + +import com.yahoo.documentmodel.NewDocumentType; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class ReservedDocumentTypeNameValidator { + + public static final List<String> ORDERED_RESERVED_NAMES = Collections.unmodifiableList( + Arrays.asList("and", "false", "id", "not", "null", "or", "true")); + public static final Set<String> RESERVED_NAMES = Collections.unmodifiableSet(new HashSet<>(ORDERED_RESERVED_NAMES)); + + public void validate(Map<String, NewDocumentType> documentDefinitions) { + List<String> conflictingNames = documentDefinitions.keySet().stream() + .filter(this::isReservedName) + .collect(Collectors.toList()); + if (!conflictingNames.isEmpty()) { + throw new IllegalArgumentException(makeReservedNameMessage(conflictingNames)); + } + } + + private boolean isReservedName(String name) { + return RESERVED_NAMES.contains(name.toLowerCase()); + } + + private static String asQuotedListString(List<String> list) { + return list.stream().map(s -> String.format("'%s'", s)).collect(Collectors.joining(", ")); + } + + private static String makeReservedNameMessage(List<String> conflictingNames) { + return String.format("The following document types conflict with reserved keyword names: %s. Reserved keywords are %s", + asQuotedListString(conflictingNames), asQuotedListString(ORDERED_RESERVED_NAMES)); + } + +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java index b4f8889690e..c7755d3de5a 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java @@ -617,6 +617,7 @@ public class ContentCluster extends AbstractConfigProducer implements StorDistri throw new IllegalArgumentException("In indexed content cluster '" + search.getClusterName() + "': Using multi-level dispatch setup is not supported when using hierarchical distribution."); } } + new ReservedDocumentTypeNameValidator().validate(documentDefinitions); new GlobalDistributionValidator().validate(documentDefinitions, globallyDistributedDocuments, redundancy); } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterTest.java index 2a3dbe002e6..0c41b8ecc0b 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ClusterTest.java @@ -25,7 +25,9 @@ import com.yahoo.vespa.model.content.utils.ContentClusterUtils; import com.yahoo.vespa.model.content.utils.SearchDefinitionBuilder; import com.yahoo.vespa.model.test.utils.ApplicationPackageUtils; import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithMockPkg; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.List; @@ -38,6 +40,8 @@ public class ClusterTest extends ContentBaseTest { private final static String HOSTS = "<admin version='2.0'><adminserver hostalias='mockhost' /></admin>"; + @Rule + public ExpectedException expectedException = ExpectedException.none(); ContentCluster parse(String xml) { xml = HOSTS + xml; @@ -787,6 +791,25 @@ public class ClusterTest extends ContentBaseTest { assertTrue(cluster.getSearch().getSearchNodes().get(0).getPreShutdownCommand().isPresent()); } + @Test + public void reserved_document_name_throws_exception() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The following document types conflict with reserved keyword names: 'true'."); + + String xml = "<content version=\"1.0\" id=\"storage\">" + + " <redundancy>1</redundancy>" + + " <documents>" + + " <document type=\"true\" mode=\"index\"/>" + + " </documents>" + + " <group>" + + " <node distribution-key=\"0\" hostalias=\"mockhost\"/>" + + " </group>" + + "</content>"; + + List<String> sds = ApplicationPackageUtils.generateSearchDefinitions("true"); + new VespaModelCreatorWithMockPkg(null, xml, sds).create(); + } + private ContentCluster createWithZone(String clusterXml, Zone zone) throws Exception { DeployState.Builder deployStateBuilder = new DeployState.Builder().properties(new DeployProperties.Builder() .hostedVespa(true) diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ReservedDocumentTypeNameValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ReservedDocumentTypeNameValidatorTest.java new file mode 100644 index 00000000000..0ad5fb3b0bd --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ReservedDocumentTypeNameValidatorTest.java @@ -0,0 +1,57 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.content; + +import com.yahoo.documentmodel.NewDocumentType; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ReservedDocumentTypeNameValidatorTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private static Map<String, NewDocumentType> asDocTypeMapping(List<String> typeNames) { + return typeNames.stream().collect(Collectors.toMap(Function.identity(), n -> new NewDocumentType(new NewDocumentType.Name(n)))); + } + + @Test + public void exception_thrown_on_reserved_names() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The following document types conflict with reserved keyword names: " + + "'and', 'false', 'id', 'not', 'null', 'or', 'true'. " + + "Reserved keywords are 'and', 'false', 'id', 'not', 'null', 'or', 'true'"); + + // Ensure ordering is consistent for testing + Map<String, NewDocumentType> orderedDocTypes = new TreeMap<>(asDocTypeMapping(ReservedDocumentTypeNameValidator.ORDERED_RESERVED_NAMES)); + + ReservedDocumentTypeNameValidator validator = new ReservedDocumentTypeNameValidator(); + validator.validate(orderedDocTypes); + } + + @Test + public void exception_is_not_thrown_on_unreserved_name() { + ReservedDocumentTypeNameValidator validator = new ReservedDocumentTypeNameValidator(); + validator.validate(asDocTypeMapping(Collections.singletonList("foo"))); + } + + @Test + public void validation_is_case_insensitive() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The following document types conflict with reserved keyword names: " + + "'NULL', 'True', 'anD'."); + + ReservedDocumentTypeNameValidator validator = new ReservedDocumentTypeNameValidator(); + Map<String, NewDocumentType> orderedDocTypes = new TreeMap<>(asDocTypeMapping(Arrays.asList("NULL", "True", "anD"))); + validator.validate(orderedDocTypes); + } + +} |