MapperService: check index.mapper.dynamic during index creation

The MapperService doesn't currently check the
index.mapper.dynamic setting during index creation,
so indices can be created with dynamic mappings even
if this setting is false. Add a check that throws an
exception in this case. Fixes #15381
This commit is contained in:
Dave 2015-12-14 09:07:38 -08:00 committed by Adrien Grand
parent 4986817c6d
commit 3f9c0fbb58
4 changed files with 124 additions and 4 deletions

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.mapper.MapperService;
/** /**
* Encapsulates the logic of whether a new index should be automatically created when * Encapsulates the logic of whether a new index should be automatically created when
@ -35,6 +36,7 @@ public final class AutoCreateIndex {
private final boolean needToCheck; private final boolean needToCheck;
private final boolean globallyDisabled; private final boolean globallyDisabled;
private final boolean dynamicMappingDisabled;
private final String[] matches; private final String[] matches;
private final String[] matches2; private final String[] matches2;
private final IndexNameExpressionResolver resolver; private final IndexNameExpressionResolver resolver;
@ -42,6 +44,7 @@ public final class AutoCreateIndex {
@Inject @Inject
public AutoCreateIndex(Settings settings, IndexNameExpressionResolver resolver) { public AutoCreateIndex(Settings settings, IndexNameExpressionResolver resolver) {
this.resolver = resolver; this.resolver = resolver;
dynamicMappingDisabled = !settings.getAsBoolean(MapperService.INDEX_MAPPER_DYNAMIC_SETTING, MapperService.INDEX_MAPPER_DYNAMIC_DEFAULT);
String value = settings.get("action.auto_create_index"); String value = settings.get("action.auto_create_index");
if (value == null || Booleans.isExplicitTrue(value)) { if (value == null || Booleans.isExplicitTrue(value)) {
needToCheck = true; needToCheck = true;
@ -82,7 +85,7 @@ public final class AutoCreateIndex {
if (exists) { if (exists) {
return false; return false;
} }
if (globallyDisabled) { if (globallyDisabled || dynamicMappingDisabled) {
return false; return false;
} }
// matches not set, default value of "true" // matches not set, default value of "true"

View File

@ -78,6 +78,8 @@ import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder;
public class MapperService extends AbstractIndexComponent implements Closeable { public class MapperService extends AbstractIndexComponent implements Closeable {
public static final String DEFAULT_MAPPING = "_default_"; public static final String DEFAULT_MAPPING = "_default_";
public static final String INDEX_MAPPER_DYNAMIC_SETTING = "index.mapper.dynamic";
public static final boolean INDEX_MAPPER_DYNAMIC_DEFAULT = true;
private static ObjectHashSet<String> META_FIELDS = ObjectHashSet.from( private static ObjectHashSet<String> META_FIELDS = ObjectHashSet.from(
"_uid", "_id", "_type", "_all", "_parent", "_routing", "_index", "_uid", "_id", "_type", "_all", "_parent", "_routing", "_index",
"_size", "_timestamp", "_ttl" "_size", "_timestamp", "_ttl"
@ -124,7 +126,7 @@ public class MapperService extends AbstractIndexComponent implements Closeable {
this.searchQuoteAnalyzer = new MapperAnalyzerWrapper(analysisService.defaultSearchQuoteAnalyzer(), p -> p.searchQuoteAnalyzer()); this.searchQuoteAnalyzer = new MapperAnalyzerWrapper(analysisService.defaultSearchQuoteAnalyzer(), p -> p.searchQuoteAnalyzer());
this.mapperRegistry = mapperRegistry; this.mapperRegistry = mapperRegistry;
this.dynamic = this.indexSettings.getSettings().getAsBoolean("index.mapper.dynamic", true); this.dynamic = this.indexSettings.getSettings().getAsBoolean(INDEX_MAPPER_DYNAMIC_SETTING, INDEX_MAPPER_DYNAMIC_DEFAULT);
defaultPercolatorMappingSource = "{\n" + defaultPercolatorMappingSource = "{\n" +
"\"_default_\":{\n" + "\"_default_\":{\n" +
"\"properties\" : {\n" + "\"properties\" : {\n" +

View File

@ -0,0 +1,114 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.index.mapper;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.index.TransportIndexAction;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.AutoCreateIndex;
import org.elasticsearch.cluster.action.shard.ShardStateAction;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.test.ESSingleNodeTestCase;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.transport.local.LocalTransport;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.test.cluster.TestClusterService;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import static org.hamcrest.CoreMatchers.instanceOf;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class DynamicMappingDisabledTests extends ESSingleNodeTestCase {
private static ThreadPool THREAD_POOL;
private TestClusterService clusterService;
private LocalTransport transport;
private TransportService transportService;
private IndicesService indicesService;
private ShardStateAction shardStateAction;
private ActionFilters actionFilters;
private IndexNameExpressionResolver indexNameExpressionResolver;
private AutoCreateIndex autoCreateIndex;
private Settings settings;
@BeforeClass
public static void createThreadPool() {
THREAD_POOL = new ThreadPool("DynamicMappingDisabledTests");
}
@Override
public void setUp() throws Exception {
super.setUp();
settings = Settings.builder()
.put(MapperService.INDEX_MAPPER_DYNAMIC_SETTING, false)
.build();
clusterService = new TestClusterService(THREAD_POOL);
transport = new LocalTransport(settings, THREAD_POOL, Version.CURRENT, new NamedWriteableRegistry());
transportService = new TransportService(transport, THREAD_POOL);
indicesService = getInstanceFromNode(IndicesService.class);
shardStateAction = new ShardStateAction(settings, clusterService, transportService, null, null);
actionFilters = new ActionFilters(Collections.emptySet());
indexNameExpressionResolver = new IndexNameExpressionResolver(settings);
autoCreateIndex = new AutoCreateIndex(settings, indexNameExpressionResolver);
}
@AfterClass
public static void destroyThreadPool() {
ThreadPool.terminate(THREAD_POOL, 30, TimeUnit.SECONDS);
// since static must set to null to be eligible for collection
THREAD_POOL = null;
}
public void testDynamicDisabled() {
TransportIndexAction action = new TransportIndexAction(settings, transportService, clusterService,
indicesService, THREAD_POOL, shardStateAction, null, null, actionFilters, indexNameExpressionResolver,
autoCreateIndex);
IndexRequest request = new IndexRequest("index", "type", "1");
request.source("foo", 3);
final AtomicBoolean onFailureCalled = new AtomicBoolean();
action.execute(request, new ActionListener<IndexResponse>() {
@Override
public void onResponse(IndexResponse indexResponse) {
fail("Indexing request should have failed");
}
@Override
public void onFailure(Throwable e) {
onFailureCalled.set(true);
assertThat(e, instanceOf(IndexNotFoundException.class));
assertEquals(e.getMessage(), "no such index");
}
});
assertTrue(onFailureCalled.get());
}
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.indices.settings;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase;
import java.util.Arrays; import java.util.Arrays;
@ -40,7 +41,7 @@ public class GetSettingsBlocksIT extends ESIntegTestCase {
.setSettings(Settings.settingsBuilder() .setSettings(Settings.settingsBuilder()
.put("index.refresh_interval", -1) .put("index.refresh_interval", -1)
.put("index.merge.policy.expunge_deletes_allowed", "30") .put("index.merge.policy.expunge_deletes_allowed", "30")
.put("index.mapper.dynamic", false))); .put(MapperService.INDEX_MAPPER_DYNAMIC_SETTING, false)));
for (String block : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) { for (String block : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE, SETTING_READ_ONLY)) {
try { try {
@ -49,7 +50,7 @@ public class GetSettingsBlocksIT extends ESIntegTestCase {
assertThat(response.getIndexToSettings().size(), greaterThanOrEqualTo(1)); assertThat(response.getIndexToSettings().size(), greaterThanOrEqualTo(1));
assertThat(response.getSetting("test", "index.refresh_interval"), equalTo("-1")); assertThat(response.getSetting("test", "index.refresh_interval"), equalTo("-1"));
assertThat(response.getSetting("test", "index.merge.policy.expunge_deletes_allowed"), equalTo("30")); assertThat(response.getSetting("test", "index.merge.policy.expunge_deletes_allowed"), equalTo("30"));
assertThat(response.getSetting("test", "index.mapper.dynamic"), equalTo("false")); assertThat(response.getSetting("test", MapperService.INDEX_MAPPER_DYNAMIC_SETTING), equalTo("false"));
} finally { } finally {
disableIndexBlock("test", block); disableIndexBlock("test", block);
} }