From a93d6d55a5ff86d39c6bbc1fb1826c1cbb4022f2 Mon Sep 17 00:00:00 2001 From: James Brook Date: Tue, 26 Nov 2013 12:32:25 +0100 Subject: [PATCH] Added support for aliases to index templates Adapted existing PR (#2739) to updated code (post #4920), added tests and docs (@javanna) Closes #1825 --- docs/reference/indices/templates.asciidoc | 29 ++ .../test/indices.put_template/10_basic.yaml | 24 ++ .../template/put/PutIndexTemplateRequest.java | 86 +++++- .../put/PutIndexTemplateRequestBuilder.java | 44 +++ .../put/TransportPutIndexTemplateAction.java | 1 + .../cluster/metadata/AliasMetaData.java | 13 +- .../cluster/metadata/AliasValidator.java | 78 ++++- .../metadata/IndexTemplateMetaData.java | 57 +++- .../metadata/MetaDataCreateIndexService.java | 36 +++ .../MetaDataIndexTemplateService.java | 25 +- .../metadata/ToAndFromJsonMetaDataTests.java | 15 +- .../IndexTemplateFileLoadingTests.java | 4 + .../template/SimpleIndexTemplateTests.java | 273 ++++++++++++++++++ .../indices/template/template0.json | 5 +- .../indices/template/template1.json | 3 + .../indices/template/template2.json | 3 + .../indices/template/template3.json | 3 + .../indices/template/template4.json | 3 + .../indices/template/template5.json | 3 + 19 files changed, 684 insertions(+), 21 deletions(-) diff --git a/docs/reference/indices/templates.asciidoc b/docs/reference/indices/templates.asciidoc index ce1c33cc66a..15559dbd800 100644 --- a/docs/reference/indices/templates.asciidoc +++ b/docs/reference/indices/templates.asciidoc @@ -27,6 +27,35 @@ Defines a template named template_1, with a template pattern of `te*`. The settings and mappings will be applied to any index name that matches the `te*` template. +coming[1.1.0] + +It is also possible to include aliases in an index template as follows: + +[source,js] +-------------------------------------------------- +curl -XPUT localhost:9200/_template/template_1 -d ' +{ + "template" : "te*", + "settings" : { + "number_of_shards" : 1 + }, + "aliases" : { + "alias1" : {}, + "alias2" : { + "filter" : { + "term" : {"user" : "kimchy" } + }, + "routing" : "kimchy" + }, + "{index}-alias" : {} <1> + } +} +' +-------------------------------------------------- + +<1> the `{index}` placeholder within the alias name will be replaced with the +actual index name that the template gets applied to during index creation. + [float] [[delete]] === Deleting a Template diff --git a/rest-api-spec/test/indices.put_template/10_basic.yaml b/rest-api-spec/test/indices.put_template/10_basic.yaml index 4c2ab84f1ef..c03ae979462 100644 --- a/rest-api-spec/test/indices.put_template/10_basic.yaml +++ b/rest-api-spec/test/indices.put_template/10_basic.yaml @@ -15,3 +15,27 @@ - match: {test.template: "test-*"} - match: {test.settings: {index.number_of_shards: '1', index.number_of_replicas: '0'}} + +--- +"Put template with aliases": + - do: + indices.put_template: + name: test + body: + template: test-* + aliases: + test_alias: {} + test_blias: {routing: b} + test_clias: {filter: {term :{user : kimchy}}} + + - do: + indices.get_template: + name: test + + - match: {test.template: "test-*"} + - length: {test.aliases: 3} + - is_true: test.aliases.test_alias + - match: {test.aliases.test_blias.index_routing: "b"} + - match: {test.aliases.test_blias.search_routing: "b"} + - match: {test.aliases.test_clias.filter.term.user: "kimchy"} + diff --git a/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java b/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java index 7a549849f5a..2989f501191 100644 --- a/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java +++ b/src/main/java/org/elasticsearch/action/admin/indices/template/put/PutIndexTemplateRequest.java @@ -21,24 +21,27 @@ package org.elasticsearch.action.admin.indices.template.put; import org.elasticsearch.ElasticsearchGenerationException; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.support.master.MasterNodeOperationRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.common.xcontent.support.XContentMapValues; import java.io.IOException; import java.util.Map; +import java.util.Set; import static com.google.common.collect.Maps.newHashMap; +import static com.google.common.collect.Sets.newHashSet; import static org.elasticsearch.action.ValidateActions.addValidationError; import static org.elasticsearch.common.settings.ImmutableSettings.Builder.EMPTY_SETTINGS; import static org.elasticsearch.common.settings.ImmutableSettings.readSettingsFromStream; @@ -63,6 +66,8 @@ public class PutIndexTemplateRequest extends MasterNodeOperationRequest mappings = newHashMap(); + private final Set aliases = newHashSet(); + private Map customs = newHashMap(); PutIndexTemplateRequest() { @@ -251,6 +256,7 @@ public class PutIndexTemplateRequest extends MasterNodeOperationRequest source = templateSource; for (Map.Entry entry : source.entrySet()) { @@ -272,6 +278,8 @@ public class PutIndexTemplateRequest extends MasterNodeOperationRequest) entry1.getValue()); } + } else if (name.equals("aliases")) { + aliases((Map) entry.getValue()); } else { // maybe custom? IndexMetaData.Custom.Factory factory = IndexMetaData.lookupFactory(name); @@ -335,6 +343,66 @@ public class PutIndexTemplateRequest extends MasterNodeOperationRequest customs() { return this.customs; } + + Set aliases() { + return this.aliases; + } + + /** + * Sets the aliases that will be associated with the index when it gets created + */ + @SuppressWarnings("unchecked") + public PutIndexTemplateRequest aliases(Map source) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.map(source); + return aliases(builder.bytes()); + } catch (IOException e) { + throw new ElasticsearchGenerationException("Failed to generate [" + source + "]", e); + } + } + + /** + * Sets the aliases that will be associated with the index when it gets created + */ + public PutIndexTemplateRequest aliases(XContentBuilder source) { + return aliases(source.bytes()); + } + + /** + * Sets the aliases that will be associated with the index when it gets created + */ + public PutIndexTemplateRequest aliases(String source) { + return aliases(new BytesArray(source)); + } + + /** + * Sets the aliases that will be associated with the index when it gets created + */ + public PutIndexTemplateRequest aliases(BytesReference source) { + try { + XContentParser parser = XContentHelper.createParser(source); + //move to the first alias + parser.nextToken(); + while ((parser.nextToken()) != XContentParser.Token.END_OBJECT) { + alias(Alias.fromXContent(parser)); + } + return this; + } catch(IOException e) { + throw new ElasticsearchParseException("Failed to parse aliases", e); + } + } + + /** + * Adds an alias that will be added when the index gets created. + * + * @param alias The metadata for the new alias + * @return the index template creation request + */ + public PutIndexTemplateRequest alias(Alias alias) { + aliases.add(alias); + return this; + } @Override public void readFrom(StreamInput in) throws IOException { @@ -355,6 +423,12 @@ public class PutIndexTemplateRequest extends MasterNodeOperationRequest mappings; + + private final ImmutableOpenMap aliases; private final ImmutableOpenMap customs; - public IndexTemplateMetaData(String name, int order, String template, Settings settings, ImmutableOpenMap mappings, ImmutableOpenMap customs) { + public IndexTemplateMetaData(String name, int order, String template, Settings settings, ImmutableOpenMap mappings, + ImmutableOpenMap aliases, ImmutableOpenMap customs) { this.name = name; this.order = order; this.template = template; this.settings = settings; this.mappings = mappings; + this.aliases = aliases; this.customs = customs; } @@ -104,6 +110,14 @@ public class IndexTemplateMetaData { return this.mappings; } + public ImmutableOpenMap aliases() { + return this.aliases; + } + + public ImmutableOpenMap getAliases() { + return this.aliases; + } + public ImmutableOpenMap customs() { return this.customs; } @@ -112,6 +126,7 @@ public class IndexTemplateMetaData { return this.customs; } + @SuppressWarnings("unchecked") public T custom(String type) { return (T) customs.get(type); } @@ -162,12 +177,15 @@ public class IndexTemplateMetaData { private Settings settings = ImmutableSettings.Builder.EMPTY_SETTINGS; private final ImmutableOpenMap.Builder mappings; + + private final ImmutableOpenMap.Builder aliases; private final ImmutableOpenMap.Builder customs; public Builder(String name) { this.name = name; mappings = ImmutableOpenMap.builder(); + aliases = ImmutableOpenMap.builder(); customs = ImmutableOpenMap.builder(); } @@ -178,6 +196,7 @@ public class IndexTemplateMetaData { settings(indexTemplateMetaData.settings()); mappings = ImmutableOpenMap.builder(indexTemplateMetaData.mappings()); + aliases = ImmutableOpenMap.builder(indexTemplateMetaData.aliases()); customs = ImmutableOpenMap.builder(indexTemplateMetaData.customs()); } @@ -219,6 +238,16 @@ public class IndexTemplateMetaData { mappings.put(mappingType, new CompressedString(mappingSource)); return this; } + + public Builder putAlias(AliasMetaData aliasMetaData) { + aliases.put(aliasMetaData.alias(), aliasMetaData); + return this; + } + + public Builder putAlias(AliasMetaData.Builder aliasMetaData) { + aliases.put(aliasMetaData.alias(), aliasMetaData.build()); + return this; + } public Builder putCustom(String type, IndexMetaData.Custom customIndexMetaData) { this.customs.put(type, customIndexMetaData); @@ -235,9 +264,10 @@ public class IndexTemplateMetaData { } public IndexTemplateMetaData build() { - return new IndexTemplateMetaData(name, order, template, settings, mappings.build(), customs.build()); + return new IndexTemplateMetaData(name, order, template, settings, mappings.build(), aliases.build(), customs.build()); } + @SuppressWarnings("unchecked") public static void toXContent(IndexTemplateMetaData indexTemplateMetaData, XContentBuilder builder, ToXContent.Params params) throws IOException { builder.startObject(indexTemplateMetaData.name(), XContentBuilder.FieldCaseConversion.NONE); @@ -280,6 +310,12 @@ public class IndexTemplateMetaData { IndexMetaData.lookupFactorySafe(cursor.key).toXContent(cursor.value, builder, params); builder.endObject(); } + + builder.startObject("aliases"); + for (ObjectCursor cursor : indexTemplateMetaData.aliases().values()) { + AliasMetaData.Builder.toXContent(cursor.value, builder, params); + } + builder.endObject(); builder.endObject(); } @@ -313,6 +349,10 @@ public class IndexTemplateMetaData { builder.putMapping(mappingType, XContentFactory.jsonBuilder().map(mappingSource).string()); } } + } else if ("aliases".equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + builder.putAlias(AliasMetaData.Builder.fromXContent(parser)); + } } else { // check if its a custom index metadata IndexMetaData.Custom.Factory factory = IndexMetaData.lookupFactory(currentFieldName); @@ -377,6 +417,13 @@ public class IndexTemplateMetaData { for (int i = 0; i < mappingsSize; i++) { builder.putMapping(in.readString(), CompressedString.readCompressedString(in)); } + if (in.getVersion().onOrAfter(Version.V_1_1_0)) { + int aliasesSize = in.readVInt(); + for (int i = 0; i < aliasesSize; i++) { + AliasMetaData aliasMd = AliasMetaData.Builder.readFrom(in); + builder.putAlias(aliasMd); + } + } int customSize = in.readVInt(); for (int i = 0; i < customSize; i++) { String type = in.readString(); @@ -396,6 +443,12 @@ public class IndexTemplateMetaData { out.writeString(cursor.key); cursor.value.writeTo(out); } + if (out.getVersion().onOrAfter(Version.V_1_1_0)) { + out.writeVInt(indexTemplateMetaData.aliases().size()); + for (ObjectCursor cursor : indexTemplateMetaData.aliases().values()) { + AliasMetaData.Builder.writeTo(cursor.value, out); + } + } out.writeVInt(indexTemplateMetaData.customs().size()); for (ObjectObjectCursor cursor : indexTemplateMetaData.customs()) { out.writeString(cursor.key); diff --git a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index 3928ce4f64b..6e4d3e6060d 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -232,6 +232,8 @@ public class MetaDataCreateIndexService extends AbstractComponent { // add the request mapping Map> mappings = Maps.newHashMap(); + Map templatesAliases = Maps.newHashMap(); + for (Map.Entry entry : request.mappings().entrySet()) { mappings.put(entry.getKey(), parseMapping(entry.getValue())); } @@ -261,6 +263,28 @@ public class MetaDataCreateIndexService extends AbstractComponent { customs.put(type, merged); } } + //handle aliases + for (ObjectObjectCursor cursor : template.aliases()) { + AliasMetaData aliasMetaData = cursor.value; + //if an alias with same name came with the create index request itself, + // ignore this one taken from the index template + if (request.aliases().contains(new Alias(aliasMetaData.alias()))) { + continue; + } + //if an alias with same name was already processed, ignore this one + if (templatesAliases.containsKey(cursor.key)) { + continue; + } + + //Allow templatesAliases to be templated by replacing a token with the name of the index that we are applying it to + if (aliasMetaData.alias().contains("{index}")) { + String templatedAlias = aliasMetaData.alias().replace("{index}", request.index()); + aliasMetaData = AliasMetaData.newAliasMetaData(aliasMetaData, templatedAlias); + } + + aliasValidator.validateAliasMetaData(aliasMetaData, request.index(), currentState.metaData()); + templatesAliases.put(aliasMetaData.alias(), aliasMetaData); + } } // now add config level mappings @@ -349,6 +373,11 @@ public class MetaDataCreateIndexService extends AbstractComponent { aliasValidator.validateAliasFilter(alias.name(), alias.filter(), indexQueryParserService); } } + for (AliasMetaData aliasMetaData : templatesAliases.values()) { + if (aliasMetaData.filter() != null) { + aliasValidator.validateAliasFilter(aliasMetaData.alias(), aliasMetaData.filter().uncompressed(), indexQueryParserService); + } + } // now, update the mappings with the actual source Map mappingsMetaData = Maps.newHashMap(); @@ -361,15 +390,22 @@ public class MetaDataCreateIndexService extends AbstractComponent { for (MappingMetaData mappingMd : mappingsMetaData.values()) { indexMetaDataBuilder.putMapping(mappingMd); } + + for (AliasMetaData aliasMetaData : templatesAliases.values()) { + indexMetaDataBuilder.putAlias(aliasMetaData); + } for (Alias alias : request.aliases()) { AliasMetaData aliasMetaData = AliasMetaData.builder(alias.name()).filter(alias.filter()) .indexRouting(alias.indexRouting()).searchRouting(alias.searchRouting()).build(); indexMetaDataBuilder.putAlias(aliasMetaData); } + for (Map.Entry customEntry : customs.entrySet()) { indexMetaDataBuilder.putCustom(customEntry.getKey(), customEntry.getValue()); } + indexMetaDataBuilder.state(request.state()); + final IndexMetaData indexMetaData; try { indexMetaData = indexMetaDataBuilder.build(); diff --git a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java index b49bc2704ea..9aeaf8a54d0 100644 --- a/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java +++ b/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java @@ -19,10 +19,12 @@ package org.elasticsearch.cluster.metadata; import com.carrotsearch.hppc.cursors.ObjectCursor; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchIllegalArgumentException; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.support.master.MasterNodeOperationRequest; import org.elasticsearch.cluster.ClusterService; import org.elasticsearch.cluster.ClusterState; @@ -39,21 +41,24 @@ import org.elasticsearch.indices.IndexTemplateAlreadyExistsException; import org.elasticsearch.indices.IndexTemplateMissingException; import org.elasticsearch.indices.InvalidIndexTemplateException; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; /** - * + * Service responsible for submitting index templates updates */ public class MetaDataIndexTemplateService extends AbstractComponent { private final ClusterService clusterService; + private final AliasValidator aliasValidator; @Inject - public MetaDataIndexTemplateService(Settings settings, ClusterService clusterService) { + public MetaDataIndexTemplateService(Settings settings, ClusterService clusterService, AliasValidator aliasValidator) { super(settings); this.clusterService = clusterService; + this.aliasValidator = aliasValidator; } public void removeTemplates(final RemoveRequest request, final RemoveListener listener) { @@ -136,6 +141,11 @@ public class MetaDataIndexTemplateService extends AbstractComponent { for (Map.Entry entry : request.mappings.entrySet()) { templateBuilder.putMapping(entry.getKey(), entry.getValue()); } + for (Alias alias : request.aliases) { + AliasMetaData aliasMetaData = AliasMetaData.builder(alias.name()).filter(alias.filter()) + .indexRouting(alias.indexRouting()).searchRouting(alias.searchRouting()).build(); + templateBuilder.putAlias(aliasMetaData); + } for (Map.Entry entry : request.customs.entrySet()) { templateBuilder.putCustom(entry.getKey(), entry.getValue()); } @@ -205,6 +215,11 @@ public class MetaDataIndexTemplateService extends AbstractComponent { if (!Strings.validFileNameExcludingAstrix(request.template)) { throw new InvalidIndexTemplateException(request.name, "template must not container the following characters " + Strings.INVALID_FILENAME_CHARS); } + + for (Alias alias : request.aliases) { + //we validate the alias only partially, as we don't know yet to which index it'll get applied to + aliasValidator.validateAliasStandalone(alias); + } } public static interface PutListener { @@ -222,6 +237,7 @@ public class MetaDataIndexTemplateService extends AbstractComponent { String template; Settings settings = ImmutableSettings.Builder.EMPTY_SETTINGS; Map mappings = Maps.newHashMap(); + List aliases = Lists.newArrayList(); Map customs = Maps.newHashMap(); TimeValue masterTimeout = MasterNodeOperationRequest.DEFAULT_MASTER_NODE_TIMEOUT; @@ -255,6 +271,11 @@ public class MetaDataIndexTemplateService extends AbstractComponent { this.mappings.putAll(mappings); return this; } + + public PutRequest aliases(Set aliases) { + this.aliases.addAll(aliases); + return this; + } public PutRequest customs(Map customs) { this.customs.putAll(customs); diff --git a/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetaDataTests.java b/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetaDataTests.java index 013c7652061..65260f73557 100644 --- a/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetaDataTests.java +++ b/src/test/java/org/elasticsearch/cluster/metadata/ToAndFromJsonMetaDataTests.java @@ -92,9 +92,13 @@ public class ToAndFromJsonMetaDataTests extends ElasticsearchTestCase { .putAlias(newAliasMetaDataBuilder("alias4").filter(ALIAS_FILTER2))) .put(IndexTemplateMetaData.builder("foo") .template("bar") - .order(1).settings(settingsBuilder() + .order(1) + .settings(settingsBuilder() .put("setting1", "value1") - .put("setting2", "value2"))) + .put("setting2", "value2")) + .putAlias(newAliasMetaDataBuilder("alias-bar1")) + .putAlias(newAliasMetaDataBuilder("alias-bar2").filter("{\"term\":{\"user\":\"kimchy\"}}")) + .putAlias(newAliasMetaDataBuilder("alias-bar3").routing("routing-bar"))) .build(); String metaDataSource = MetaData.Builder.toXContent(metaData); @@ -184,6 +188,13 @@ public class ToAndFromJsonMetaDataTests extends ElasticsearchTestCase { assertThat(parsedMetaData.templates().get("foo").template(), is("bar")); assertThat(parsedMetaData.templates().get("foo").settings().get("index.setting1"), is("value1")); assertThat(parsedMetaData.templates().get("foo").settings().getByPrefix("index.").get("setting2"), is("value2")); + assertThat(parsedMetaData.templates().get("foo").aliases().size(), equalTo(3)); + assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar1").alias(), equalTo("alias-bar1")); + assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar2").alias(), equalTo("alias-bar2")); + assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar2").filter().string(), equalTo("{\"term\":{\"user\":\"kimchy\"}}")); + assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar3").alias(), equalTo("alias-bar3")); + assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar3").indexRouting(), equalTo("routing-bar")); + assertThat(parsedMetaData.templates().get("foo").aliases().get("alias-bar3").searchRouting(), equalTo("routing-bar")); } private static final String MAPPING_SOURCE1 = "{\"mapping1\":{\"text1\":{\"type\":\"string\"}}}"; diff --git a/src/test/java/org/elasticsearch/indices/template/IndexTemplateFileLoadingTests.java b/src/test/java/org/elasticsearch/indices/template/IndexTemplateFileLoadingTests.java index ddab5ea7152..bc0971e7ad3 100644 --- a/src/test/java/org/elasticsearch/indices/template/IndexTemplateFileLoadingTests.java +++ b/src/test/java/org/elasticsearch/indices/template/IndexTemplateFileLoadingTests.java @@ -34,6 +34,7 @@ import java.io.File; import java.util.HashSet; import java.util.Set; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; /** @@ -84,6 +85,9 @@ public class IndexTemplateFileLoadingTests extends ElasticsearchIntegrationTest ClusterStateResponse stateResponse = client().admin().cluster().prepareState().setIndices(indexName).get(); assertThat(stateResponse.getState().getMetaData().indices().get(indexName).getNumberOfShards(), is(10)); assertThat(stateResponse.getState().getMetaData().indices().get(indexName).getNumberOfReplicas(), is(0)); + assertThat(stateResponse.getState().getMetaData().indices().get(indexName).aliases().size(), equalTo(1)); + String aliasName = indexName + "-alias"; + assertThat(stateResponse.getState().getMetaData().indices().get(indexName).aliases().get(aliasName).alias(), equalTo(aliasName)); } } } diff --git a/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateTests.java b/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateTests.java index 3d41e4b2a99..24eb37b14cb 100644 --- a/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateTests.java +++ b/src/test/java/org/elasticsearch/indices/template/SimpleIndexTemplateTests.java @@ -19,22 +19,34 @@ package org.elasticsearch.indices.template; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.admin.indices.alias.Alias; +import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; +import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequestBuilder; import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.common.Priority; import org.elasticsearch.common.settings.ImmutableSettings; import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.index.query.FilterBuilders; +import org.elasticsearch.index.query.QueryParsingException; import org.elasticsearch.indices.IndexTemplateAlreadyExistsException; +import org.elasticsearch.indices.InvalidAliasNameException; +import org.elasticsearch.search.SearchHit; import org.elasticsearch.test.ElasticsearchIntegrationTest; import org.junit.Test; import java.util.Arrays; import java.util.List; +import java.util.Set; import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertThrows; import static org.hamcrest.Matchers.*; @@ -322,4 +334,265 @@ public class SimpleIndexTemplateTests extends ElasticsearchIntegrationTest { GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings("test").get(); assertThat(getSettingsResponse.getIndexToSettings().get("test").getAsMap().get("index.does_not_exist"), equalTo("test")); } + + public void testIndexTemplateWithAliases() throws Exception { + + client().admin().indices().preparePutTemplate("template_with_aliases") + .setTemplate("te*") + .addAlias(new Alias("simple_alias")) + .addAlias(new Alias("templated_alias-{index}")) + .addAlias(new Alias("filtered_alias").filter("{\"type\":{\"value\":\"type2\"}}")) + .addAlias(new Alias("complex_filtered_alias") + .filter(FilterBuilders.termsFilter("_type", "typeX", "typeY", "typeZ").execution("bool").cache(true))) + .get(); + + client().prepareIndex("test_index", "type1", "1").setSource("field", "A value").get(); + client().prepareIndex("test_index", "type2", "2").setSource("field", "B value").get(); + client().prepareIndex("test_index", "typeX", "3").setSource("field", "C value").get(); + client().prepareIndex("test_index", "typeY", "4").setSource("field", "D value").get(); + client().prepareIndex("test_index", "typeZ", "5").setSource("field", "E value").get(); + + GetAliasesResponse getAliasesResponse = client().admin().indices().prepareGetAliases().setIndices("test_index").get(); + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get("test_index").size(), equalTo(4)); + + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test_index").get(); + assertHitCount(searchResponse, 5l); + + searchResponse = client().prepareSearch("simple_alias").get(); + assertHitCount(searchResponse, 5l); + + searchResponse = client().prepareSearch("templated_alias-test_index").get(); + assertHitCount(searchResponse, 5l); + + searchResponse = client().prepareSearch("filtered_alias").get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).type(), equalTo("type2")); + + // Search the complex filter alias + searchResponse = client().prepareSearch("complex_filtered_alias").get(); + assertHitCount(searchResponse, 3l); + + Set types = Sets.newHashSet(); + for (SearchHit searchHit : searchResponse.getHits().getHits()) { + types.add(searchHit.getType()); + } + assertThat(types.size(), equalTo(3)); + assertThat(types, containsInAnyOrder("typeX", "typeY", "typeZ")); + } + + @Test + public void testIndexTemplateWithAliasesInSource() { + client().admin().indices().preparePutTemplate("template_1") + .setSource("{\n" + + " \"template\" : \"*\",\n" + + " \"aliases\" : {\n" + + " \"my_alias\" : {\n" + + " \"filter\" : {\n" + + " \"type\" : {\n" + + " \"value\" : \"type2\"\n" + + " }\n" + + " }\n" + + " }\n" + + " }\n" + + "}").get(); + + + createIndex("test_index"); + ensureGreen(); + + GetAliasesResponse getAliasesResponse = client().admin().indices().prepareGetAliases().setIndices("test_index").get(); + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get("test_index").size(), equalTo(1)); + + client().prepareIndex("test_index", "type1", "1").setSource("field", "value1").get(); + client().prepareIndex("test_index", "type2", "2").setSource("field", "value2").get(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test_index").get(); + assertHitCount(searchResponse, 2l); + + searchResponse = client().prepareSearch("my_alias").get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).type(), equalTo("type2")); + } + + @Test + public void testIndexTemplateWithAliasesSource() { + client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .setAliases( + " {\n" + + " \"alias1\" : {},\n" + + " \"alias2\" : {\n" + + " \"filter\" : {\n" + + " \"type\" : {\n" + + " \"value\" : \"type2\"\n" + + " }\n" + + " }\n" + + " },\n" + + " \"alias3\" : { \"routing\" : \"1\" }" + + " }\n").get(); + + createIndex("test_index"); + ensureGreen(); + + GetAliasesResponse getAliasesResponse = client().admin().indices().prepareGetAliases().setIndices("test_index").get(); + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get("test_index").size(), equalTo(3)); + + client().prepareIndex("test_index", "type1", "1").setSource("field", "value1").get(); + client().prepareIndex("test_index", "type2", "2").setSource("field", "value2").get(); + refresh(); + + SearchResponse searchResponse = client().prepareSearch("test_index").get(); + assertHitCount(searchResponse, 2l); + + searchResponse = client().prepareSearch("alias1").get(); + assertHitCount(searchResponse, 2l); + + searchResponse = client().prepareSearch("alias2").get(); + assertHitCount(searchResponse, 1l); + assertThat(searchResponse.getHits().getAt(0).type(), equalTo("type2")); + } + + @Test + public void testDuplicateAlias() throws Exception { + client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .addAlias(new Alias("my_alias").filter(FilterBuilders.termFilter("field", "value1"))) + .addAlias(new Alias("my_alias").filter(FilterBuilders.termFilter("field", "value2"))) + .get(); + + GetIndexTemplatesResponse response = client().admin().indices().prepareGetTemplates("template_1").get(); + assertThat(response.getIndexTemplates().size(), equalTo(1)); + assertThat(response.getIndexTemplates().get(0).getAliases().size(), equalTo(1)); + assertThat(response.getIndexTemplates().get(0).getAliases().get("my_alias").filter().string(), containsString("\"value1\"")); + } + + @Test + public void testAliasInvalidFilterValidJson() throws Exception { + + //invalid filter but valid json: put index template works fine, fails during index creation + client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .addAlias(new Alias("invalid_alias").filter("{ \"invalid\": {} }")).get(); + + GetIndexTemplatesResponse response = client().admin().indices().prepareGetTemplates("template_1").get(); + assertThat(response.getIndexTemplates().size(), equalTo(1)); + assertThat(response.getIndexTemplates().get(0).getAliases().size(), equalTo(1)); + assertThat(response.getIndexTemplates().get(0).getAliases().get("invalid_alias").filter().string(), equalTo("{\"invalid\":{}}")); + + try { + createIndex("test"); + fail("index creation should have failed due to invalid alias filter in matching index template"); + } catch(ElasticsearchIllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("failed to parse filter for alias [invalid_alias]")); + assertThat(e.getCause(), instanceOf(QueryParsingException.class)); + assertThat(e.getCause().getMessage(), equalTo("[test] No filter registered for [invalid]")); + } + } + + @Test + public void testAliasInvalidFilterInvalidJson() throws Exception { + + //invalid json: put index template fails + PutIndexTemplateRequestBuilder putIndexTemplateRequestBuilder = client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .addAlias(new Alias("invalid_alias").filter("abcde")); + + try { + putIndexTemplateRequestBuilder.get(); + } catch(ElasticsearchIllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("failed to parse filter for alias [invalid_alias]")); + } + + GetIndexTemplatesResponse response = client().admin().indices().prepareGetTemplates("template_1").get(); + assertThat(response.getIndexTemplates().size(), equalTo(0)); + } + + @Test + public void testAliasNameExistingIndex() throws Exception { + + createIndex("index"); + + client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .addAlias(new Alias("index")).get(); + + try { + createIndex("test"); + fail("index creation should have failed due to alias with existing index name in mathching index template"); + } catch(InvalidAliasNameException e) { + assertThat(e.getMessage(), equalTo("[test] Invalid alias name [index], an index exists with the same name as the alias")); + } + } + + @Test + public void testAliasEmptyName() throws Exception { + PutIndexTemplateRequestBuilder putIndexTemplateRequestBuilder = client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .addAlias(new Alias(" ").indexRouting("1,2,3")); + + try { + putIndexTemplateRequestBuilder.get(); + fail("put template should have failed due to alias with empty name"); + } catch (ElasticsearchIllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("alias name is required")); + } + } + + @Test + public void testAliasWithMultipleIndexRoutings() throws Exception { + PutIndexTemplateRequestBuilder putIndexTemplateRequestBuilder = client().admin().indices().preparePutTemplate("template_1") + .setTemplate("te*") + .addAlias(new Alias("alias").indexRouting("1,2,3")); + + try { + putIndexTemplateRequestBuilder.get(); + fail("put template should have failed due to alias with multiple index routings"); + } catch (ElasticsearchIllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("alias [alias] has several index routing values associated with it")); + } + } + + @Test + public void testMultipleAliasesPrecedence() throws Exception { + client().admin().indices().preparePutTemplate("template1") + .setTemplate("*") + .setOrder(0) + .addAlias(new Alias("alias1")) + .addAlias(new Alias("{index}-alias")) + .addAlias(new Alias("alias3").filter(FilterBuilders.missingFilter("test"))) + .addAlias(new Alias("alias4")).get(); + + client().admin().indices().preparePutTemplate("template2") + .setTemplate("te*") + .setOrder(1) + .addAlias(new Alias("alias1").routing("test")) + .addAlias(new Alias("alias3")).get(); + + + assertAcked(prepareCreate("test").addAlias(new Alias("test-alias").searchRouting("test-routing"))); + + ensureGreen(); + + GetAliasesResponse getAliasesResponse = client().admin().indices().prepareGetAliases().addIndices("test").get(); + assertThat(getAliasesResponse.getAliases().get("test").size(), equalTo(4)); + + for (AliasMetaData aliasMetaData : getAliasesResponse.getAliases().get("test")) { + assertThat(aliasMetaData.alias(), anyOf(equalTo("alias1"), equalTo("test-alias"), equalTo("alias3"), equalTo("alias4"))); + if ("alias1".equals(aliasMetaData.alias())) { + assertThat(aliasMetaData.indexRouting(), equalTo("test")); + assertThat(aliasMetaData.searchRouting(), equalTo("test")); + } else if ("alias3".equals(aliasMetaData.alias())) { + assertThat(aliasMetaData.filter(), nullValue()); + } else if ("test-alias".equals(aliasMetaData.alias())) { + assertThat(aliasMetaData.indexRouting(), nullValue()); + assertThat(aliasMetaData.searchRouting(), equalTo("test-routing")); + } + } + } } diff --git a/src/test/java/org/elasticsearch/indices/template/template0.json b/src/test/java/org/elasticsearch/indices/template/template0.json index 3b2ace1389b..c80ba92340d 100644 --- a/src/test/java/org/elasticsearch/indices/template/template0.json +++ b/src/test/java/org/elasticsearch/indices/template/template0.json @@ -3,5 +3,8 @@ "settings" : { "index.number_of_shards": 10, "index.number_of_replicas": 0 - } + }, + "aliases" : { + "{index}-alias" : {} + } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/indices/template/template1.json b/src/test/java/org/elasticsearch/indices/template/template1.json index f91866865e7..5b54e404360 100644 --- a/src/test/java/org/elasticsearch/indices/template/template1.json +++ b/src/test/java/org/elasticsearch/indices/template/template1.json @@ -3,5 +3,8 @@ "settings" : { "number_of_shards": 10, "number_of_replicas": 0 + }, + "aliases" : { + "{index}-alias" : {} } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/indices/template/template2.json b/src/test/java/org/elasticsearch/indices/template/template2.json index c48169f15a5..87713447e2e 100644 --- a/src/test/java/org/elasticsearch/indices/template/template2.json +++ b/src/test/java/org/elasticsearch/indices/template/template2.json @@ -5,5 +5,8 @@ "number_of_shards": 10, "number_of_replicas": 0 } + }, + "aliases" : { + "{index}-alias" : {} } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/indices/template/template3.json b/src/test/java/org/elasticsearch/indices/template/template3.json index 3114cd616eb..762eb0f236a 100644 --- a/src/test/java/org/elasticsearch/indices/template/template3.json +++ b/src/test/java/org/elasticsearch/indices/template/template3.json @@ -4,6 +4,9 @@ "settings" : { "index.number_of_shards": 10, "index.number_of_replicas": 0 + }, + "aliases" : { + "{index}-alias" : {} } } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/indices/template/template4.json b/src/test/java/org/elasticsearch/indices/template/template4.json index 674f6310470..31978c7477a 100644 --- a/src/test/java/org/elasticsearch/indices/template/template4.json +++ b/src/test/java/org/elasticsearch/indices/template/template4.json @@ -4,6 +4,9 @@ "settings" : { "number_of_shards": 10, "number_of_replicas": 0 + }, + "aliases" : { + "{index}-alias" : {} } } } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/indices/template/template5.json b/src/test/java/org/elasticsearch/indices/template/template5.json index c8192c24348..8512a7c5b9f 100644 --- a/src/test/java/org/elasticsearch/indices/template/template5.json +++ b/src/test/java/org/elasticsearch/indices/template/template5.json @@ -6,6 +6,9 @@ "number_of_shards": 10, "number_of_replicas": 0 } + }, + "aliases" : { + "{index}-alias" : {} } } } \ No newline at end of file