From 84134666db23f4528f67070a47f0e374bbb665ee Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 19 Nov 2018 15:43:29 +0100 Subject: [PATCH] Add global and index level blocks to IndexSettings --- .../metadata/MetaDataCreateIndexService.java | 7 ++- .../metadata/MetaDataIndexAliasesService.java | 9 ++- .../MetaDataIndexTemplateService.java | 2 +- .../metadata/MetaDataMappingService.java | 9 ++- .../org/elasticsearch/index/IndexService.java | 6 ++ .../elasticsearch/index/IndexSettings.java | 60 ++++++++++++++++++- .../elasticsearch/indices/IndicesService.java | 15 +++-- .../cluster/IndicesClusterStateService.java | 35 +++++++++-- .../metadata/IndexCreationTaskTests.java | 2 +- .../index/IndexSettingsTests.java | 56 +++++++++++++++++ ...dicesLifecycleListenerSingleNodeTests.java | 11 +++- .../indices/IndicesServiceTests.java | 7 ++- ...actIndicesClusterStateServiceTestCase.java | 24 ++++++-- .../indices/cluster/ClusterStateChanges.java | 5 +- ...ClusterStateServiceRandomUpdatesTests.java | 28 +++++++++ 15 files changed, 249 insertions(+), 27 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java index 3bb6c38dd1d9e..b68ff228b841f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java @@ -447,8 +447,13 @@ public ClusterState execute(ClusterState currentState) throws Exception { "]: cannot be greater than number of shard copies [" + (tmpImd.getNumberOfReplicas() + 1) + "]"); } + + final ClusterBlocks clusterBlocks = currentState.blocks(); + final Set globalBlocks = clusterBlocks.global(); + final Set indexBlocks = clusterBlocks.indices().get(tmpImd.getIndex().getName()); + // create the index here (on the master) to validate it can be created, as well as adding the mapping - final IndexService indexService = indicesService.createIndex(tmpImd, Collections.emptyList()); + final IndexService indexService = indicesService.createIndex(tmpImd, globalBlocks, indexBlocks, Collections.emptyList()); createdIndex = indexService.index(); // now add the mappings MapperService mapperService = indexService.mapperService(); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java index e6d0fc0832445..9caea76f9faa5 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexAliasesService.java @@ -25,6 +25,8 @@ import org.elasticsearch.cluster.AckedClusterStateUpdateTask; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.AliasAction.NewAliasValidator; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; @@ -124,6 +126,11 @@ ClusterState innerExecute(ClusterState currentState, Iterable actio if (index == null) { throw new IndexNotFoundException(action.getIndex()); } + + final ClusterBlocks clusterBlocks = currentState.blocks(); + final Set globalBlocks = clusterBlocks.global(); + final Set indexBlocks = clusterBlocks.indices().get(index.getIndex().getName()); + NewAliasValidator newAliasValidator = (alias, indexRouting, filter, writeIndex) -> { /* It is important that we look up the index using the metadata builder we are modifying so we can remove an * index and replace it with an alias. */ @@ -136,7 +143,7 @@ ClusterState innerExecute(ClusterState currentState, Iterable actio if (indexService == null) { // temporarily create the index and add mappings so we can parse the filter try { - indexService = indicesService.createIndex(index, emptyList()); + indexService = indicesService.createIndex(index, globalBlocks, indexBlocks, emptyList()); indicesToClose.add(index.getIndex()); } catch (IOException e) { throw new ElasticsearchException("Failed to create temporary index for parsing the alias", e); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java index 5759a8aef35ff..c1b3d4c5b1304 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java @@ -232,7 +232,7 @@ private static void validateAndAddTemplate(final PutRequest request, IndexTempla .build(); final IndexMetaData tmpIndexMetadata = IndexMetaData.builder(temporaryIndexName).settings(dummySettings).build(); - IndexService dummyIndexService = indicesService.createIndex(tmpIndexMetadata, Collections.emptyList()); + IndexService dummyIndexService = indicesService.createIndex(tmpIndexMetadata, null, null, Collections.emptyList()); createdIndex = dummyIndexService.index(); templateBuilder.order(request.order); diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java index 1832d73524161..2e4a0a82578a8 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java @@ -22,6 +22,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingClusterStateUpdateRequest; @@ -52,6 +54,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import static org.elasticsearch.indices.cluster.IndicesClusterStateService.AllocatedIndices.IndexRemovalReason.NO_LONGER_ASSIGNED; @@ -144,8 +147,12 @@ ClusterState executeRefresh(final ClusterState currentState, final List globalBlocks = clusterBlocks.global(); + final Set indexBlocks = clusterBlocks.indices().get(indexMetaData.getIndex().getName()); + // we need to create the index here, and add the current mapping to it, so we can merge - indexService = indicesService.createIndex(indexMetaData, Collections.emptyList()); + indexService = indicesService.createIndex(indexMetaData, globalBlocks, indexBlocks, Collections.emptyList()); removeIndex = true; indexService.mapperService().merge(indexMetaData, MergeReason.MAPPING_RECOVERY); } diff --git a/server/src/main/java/org/elasticsearch/index/IndexService.java b/server/src/main/java/org/elasticsearch/index/IndexService.java index c34a5228b7f6b..6c5366a92f126 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexService.java +++ b/server/src/main/java/org/elasticsearch/index/IndexService.java @@ -28,6 +28,7 @@ import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; @@ -679,6 +680,11 @@ public boolean isForceExecution() { } } + @Override + public void updateBlocks(@Nullable final Set globalBlocks, @Nullable final Set indexBlocks) { + indexSettings.updateIndexBlocks(globalBlocks, indexBlocks); + } + private void rescheduleFsyncTask(Translog.Durability durability) { try { if (fsyncTask != null) { diff --git a/server/src/main/java/org/elasticsearch/index/IndexSettings.java b/server/src/main/java/org/elasticsearch/index/IndexSettings.java index a835c8bd5b416..7431b3987ce2f 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexSettings.java +++ b/server/src/main/java/org/elasticsearch/index/IndexSettings.java @@ -21,7 +21,10 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.index.MergePolicy; import org.elasticsearch.Version; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; @@ -37,6 +40,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; @@ -327,6 +331,11 @@ public final class IndexSettings { private volatile String defaultPipeline; private volatile boolean searchThrottled; + /** + * A {@link ClusterBlocks} containing global level and index level blocks + */ + private volatile ClusterBlocks indexBlocks; + /** * The maximum number of refresh listeners allows on this shard. */ @@ -397,8 +406,27 @@ public IndexSettings(final IndexMetaData indexMetaData, final Settings nodeSetti * * @param indexMetaData the index metadata this settings object is associated with * @param nodeSettings the nodes settings this index is allocated on. + * @param indexScopedSettings the index level settings */ public IndexSettings(final IndexMetaData indexMetaData, final Settings nodeSettings, IndexScopedSettings indexScopedSettings) { + this(indexMetaData, nodeSettings, indexScopedSettings, null, null); + } + + /** + * Creates a new {@link IndexSettings} instance. The given node settings will be merged with the settings in the metadata + * while index level settings will overwrite node settings. + * + * @param indexMetaData the index metadata this settings object is associated with + * @param nodeSettings the nodes settings this index is allocated on. + * @param indexScopedSettings the index level settings + * @param globalBlocks the global level blocks + * @param indexBlocks the index level blocks + */ + public IndexSettings(final IndexMetaData indexMetaData, + final Settings nodeSettings, + final IndexScopedSettings indexScopedSettings, + @Nullable final Set globalBlocks, + @Nullable final Set indexBlocks) { scopedSettings = indexScopedSettings.copy(nodeSettings, indexMetaData); this.nodeSettings = nodeSettings; this.settings = Settings.builder().put(nodeSettings).put(indexMetaData.getSettings()).build(); @@ -408,7 +436,7 @@ public IndexSettings(final IndexMetaData indexMetaData, final Settings nodeSetti nodeName = Node.NODE_NAME_SETTING.get(settings); this.indexMetaData = indexMetaData; numberOfShards = settings.getAsInt(IndexMetaData.SETTING_NUMBER_OF_SHARDS, null); - + this.indexBlocks = buildBlocks(globalBlocks, indexBlocks); this.searchThrottled = INDEX_SEARCH_THROTTLED.get(settings); this.queryStringLenient = QUERY_STRING_LENIENT_SETTING.get(settings); this.queryStringAnalyzeWildcard = QUERY_STRING_ANALYZE_WILDCARD.get(nodeSettings); @@ -626,6 +654,36 @@ public static boolean same(final Settings left, final Settings right) { .equals(right.filter(IndexScopedSettings.INDEX_SETTINGS_KEY_PREDICATE)); } + /** + * Updates the global level and index level blocks. + * + * @param globalBlocks the global level blocks + * @param indexBlocks the index level blocks + */ + public synchronized void updateIndexBlocks(@Nullable final Set globalBlocks, + @Nullable final Set indexBlocks) { + this.indexBlocks = buildBlocks(globalBlocks, indexBlocks); + } + + private ClusterBlocks buildBlocks(@Nullable final Set globalBlocks, + @Nullable final Set indexBlocks) { + final ClusterBlocks.Builder builder = ClusterBlocks.builder(); + if (globalBlocks != null) { + globalBlocks.forEach(builder::addGlobalBlock); + } + if (indexBlocks != null) { + indexBlocks.forEach(block -> builder.addIndexBlock(index.getName(), block)); + } + return builder.build(); + } + + /** + * @return the current global level and index level blocks + */ + public ClusterBlocks getIndexBlocks() { + return indexBlocks; + } + /** * Returns the translog durability for this index. */ diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesService.java b/server/src/main/java/org/elasticsearch/indices/IndicesService.java index ae108da7534a7..eb232e7c0ae26 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesService.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesService.java @@ -36,6 +36,7 @@ import org.elasticsearch.action.search.SearchType; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MetaData; @@ -448,8 +449,10 @@ public IndexService indexServiceSafe(Index index) { * @throws ResourceAlreadyExistsException if the index already exists. */ @Override - public synchronized IndexService createIndex( - final IndexMetaData indexMetaData, final List builtInListeners) throws IOException { + public synchronized IndexService createIndex(final IndexMetaData indexMetaData, + @Nullable final Set globalBlocks, + @Nullable final Set indexBlocks, + final List builtInListeners) throws IOException { ensureChangesAllowed(); if (indexMetaData.getIndexUUID().equals(IndexMetaData.INDEX_UUID_NA_VALUE)) { throw new IllegalArgumentException("index must have a real UUID found value: [" + indexMetaData.getIndexUUID() + "]"); @@ -471,6 +474,8 @@ public void onStoreClosed(ShardId shardId) { createIndexService( "create index", indexMetaData, + globalBlocks, + indexBlocks, indicesQueryCache, indicesFieldDataCache, finalListeners, @@ -493,11 +498,13 @@ public void onStoreClosed(ShardId shardId) { */ private synchronized IndexService createIndexService(final String reason, IndexMetaData indexMetaData, + @Nullable final Set globalBlocks, + @Nullable final Set indexBlocks, IndicesQueryCache indicesQueryCache, IndicesFieldDataCache indicesFieldDataCache, List builtInListeners, IndexingOperationListener... indexingOperationListeners) throws IOException { - final IndexSettings idxSettings = new IndexSettings(indexMetaData, settings, indexScopedSettings); + final IndexSettings idxSettings = new IndexSettings(indexMetaData, settings, indexScopedSettings, globalBlocks, indexBlocks); // we ignore private settings since they are not registered settings indexScopedSettings.validate(indexMetaData.getSettings(), true, true, true); logger.debug("creating Index [{}], shards [{}]/[{}] - reason [{}]", @@ -587,7 +594,7 @@ public synchronized void verifyIndexMetadata(IndexMetaData metaData, IndexMetaDa closeables.add(indicesQueryCache); // this will also fail if some plugin fails etc. which is nice since we can verify that early final IndexService service = - createIndexService("metadata verification", metaData, indicesQueryCache, indicesFieldDataCache, emptyList()); + createIndexService("metadata verification", metaData, null, null, indicesQueryCache, indicesFieldDataCache, emptyList()); closeables.add(() -> service.close("metadata verification", false)); service.mapperService().merge(metaData, MapperService.MergeReason.MAPPING_RECOVERY); if (metaData.equals(metaDataUpdate) == false) { diff --git a/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java b/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java index cd32c415ea54b..c71286f5aa606 100644 --- a/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java +++ b/server/src/main/java/org/elasticsearch/indices/cluster/IndicesClusterStateService.java @@ -30,6 +30,8 @@ import org.elasticsearch.cluster.ClusterStateApplier; import org.elasticsearch.cluster.action.index.NodeMappingRefreshAction; import org.elasticsearch.cluster.action.shard.ShardStateAction; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; @@ -457,7 +459,11 @@ private void createIndices(final ClusterState state) { AllocatedIndex indexService = null; try { - indexService = indicesService.createIndex(indexMetaData, buildInIndexListener); + final ClusterBlocks clusterBlocks = state.blocks(); + final Set globalBlocks = clusterBlocks.global(); + final Set indexBlocks = clusterBlocks.indices().get(index.getName()); + + indexService = indicesService.createIndex(indexMetaData, globalBlocks, indexBlocks, buildInIndexListener); if (indexService.updateMapping(null, indexMetaData) && sendRefreshMapping) { nodeMappingRefreshAction.nodeMappingRefresh(state.nodes().getMasterNode(), new NodeMappingRefreshAction.NodeMappingRefreshRequest(indexMetaData.getIndex().getName(), @@ -479,8 +485,8 @@ private void createIndices(final ClusterState state) { } } - private void updateIndices(ClusterChangedEvent event) { - if (!event.metaDataChanged()) { + private void updateIndices(final ClusterChangedEvent event) { + if (event.metaDataChanged() == false && event.blocksChanged() == false) { return; } final ClusterState state = event.state(); @@ -512,6 +518,11 @@ private void updateIndices(ClusterChangedEvent event) { } } } + + if (event.blocksChanged()) { + final ClusterBlocks clusterBlocks = state.blocks(); + indexService.updateBlocks(clusterBlocks.global(), clusterBlocks.indices().get(index.getName())); + } } } @@ -780,6 +791,14 @@ public interface AllocatedIndex extends Iterable, IndexCompo */ void updateMetaData(IndexMetaData currentIndexMetaData, IndexMetaData newIndexMetaData); + /** + * Updates the global level and index level blocks of this index. Changes become visible through {@link #getIndexSettings()}. + * + * @param globalBlocks the global level blocks + * @param indexBlocks the index level blocks + */ + void updateBlocks(@Nullable Set globalBlocks, @Nullable Set indexBlocks); + /** * Checks if index requires refresh from master. */ @@ -801,12 +820,16 @@ public interface AllocatedIndices> /** * Creates a new {@link IndexService} for the given metadata. * - * @param indexMetaData the index metadata to create the index for - * @param builtInIndexListener a list of built-in lifecycle {@link IndexEventListener} that should should be used along side with - * the per-index listeners + * @param indexMetaData the index metadata to create the index for + * @param globalBlocks the global level blocks + * @param indexBlocks the index level blocks + * @param builtInIndexListener a list of built-in lifecycle {@link IndexEventListener} that should should be used along side with + * the per-index listeners * @throws ResourceAlreadyExistsException if the index already exists. */ U createIndex(IndexMetaData indexMetaData, + @Nullable Set globalBlocks, + @Nullable Set indexBlocks, List builtInIndexListener) throws IOException; /** diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexCreationTaskTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexCreationTaskTests.java index 518a60ffe38f6..15420e9176427 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexCreationTaskTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexCreationTaskTests.java @@ -457,6 +457,6 @@ private void setupIndicesService() throws Exception { when(service.getIndexSortSupplier()).thenReturn(supplier); when(service.getIndexEventListener()).thenReturn(mock(IndexEventListener.class)); - when(indicesService.createIndex(anyObject(), anyObject())).thenReturn(service); + when(indicesService.createIndex(anyObject(), anyObject(), anyObject(), anyObject())).thenReturn(service); } } diff --git a/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java b/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java index 64a2fa69bcbd5..6b5d9346cb69c 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexSettingsTests.java @@ -20,6 +20,8 @@ package org.elasticsearch.index; import org.elasticsearch.Version; +import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.AbstractScopedSettings; import org.elasticsearch.common.settings.IndexScopedSettings; @@ -29,6 +31,7 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.translog.Translog; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; @@ -41,6 +44,7 @@ import java.util.function.Function; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.core.StringContains.containsString; import static org.hamcrest.object.HasToString.hasToString; @@ -561,4 +565,56 @@ public void testUpdateSoftDeletesFails() { Settings.builder(), Settings.builder(), "index")); assertThat(error.getMessage(), equalTo("final index setting [index.soft_deletes.enabled], not updateable")); } + + public void testIndexSettingsWithoutBlocks() { + IndexScopedSettings indexScopedSettings = new IndexScopedSettings(Settings.EMPTY, IndexScopedSettings.BUILT_IN_INDEX_SETTINGS); + IndexMetaData metaData = newIndexMeta("index", Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_INDEX_UUID, "0xdeadbeef") + .build()); + + Set globalBlocks = randomBoolean() ? Collections.emptySet() : null; + Set indexBlocks = randomBoolean() ? Collections.emptySet() : null; + + IndexSettings indexSettings = new IndexSettings(metaData, Settings.EMPTY, indexScopedSettings, globalBlocks, indexBlocks); + assertThat(indexSettings.getIndexBlocks().global(), notNullValue()); + assertThat(indexSettings.getIndexBlocks().global().size(), equalTo(0)); + assertThat(indexSettings.getIndexBlocks().indices(), notNullValue()); + assertThat(indexSettings.getIndexBlocks().indices().size(), equalTo(0)); + } + + public void testIndexSettingsWithBlocks() { + IndexScopedSettings indexScopedSettings = new IndexScopedSettings(Settings.EMPTY, IndexScopedSettings.BUILT_IN_INDEX_SETTINGS); + IndexMetaData metaData = newIndexMeta("index", Settings.builder() + .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetaData.SETTING_INDEX_UUID, "0xdeadbeef") + .build()); + + Set globalBlocks = randomClusterBlocks(); + Set indexBlocks = randomClusterBlocks(); + + IndexSettings indexSettings = new IndexSettings(metaData, Settings.EMPTY, indexScopedSettings, globalBlocks, indexBlocks); + assertThat(indexSettings.getIndexBlocks().global(), equalTo(globalBlocks)); + assertThat(indexSettings.getIndexBlocks().indices(), notNullValue()); + assertThat(indexSettings.getIndexBlocks().indices().size(), equalTo(1)); + assertThat(indexSettings.getIndexBlocks().indices().get("index"), equalTo(indexBlocks)); + + globalBlocks = randomClusterBlocks(); + indexBlocks = randomClusterBlocks(); + indexSettings.updateIndexBlocks(globalBlocks, indexBlocks); + + assertThat(indexSettings.getIndexBlocks().global(), equalTo(globalBlocks)); + assertThat(indexSettings.getIndexBlocks().indices(), notNullValue()); + assertThat(indexSettings.getIndexBlocks().indices().size(), equalTo(1)); + assertThat(indexSettings.getIndexBlocks().indices().get("index"), equalTo(indexBlocks)); + } + + private Set randomClusterBlocks() { + final Set globalBlocks = new HashSet<>(); + for (int i = 0; i < randomIntBetween(1, 5); i++) { + globalBlocks.add(new ClusterBlock(i, "random block", randomBoolean(), randomBoolean(), randomBoolean(), + randomFrom(RestStatus.values()), ClusterBlockLevel.ALL)); + } + return globalBlocks; + } } diff --git a/server/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java b/server/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java index 769cdfc8a9b53..bba0e0dbece5a 100644 --- a/server/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java +++ b/server/src/test/java/org/elasticsearch/indices/IndicesLifecycleListenerSingleNodeTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.indices; import org.elasticsearch.Version; +import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.RecoverySource; @@ -38,6 +39,7 @@ import org.elasticsearch.test.ESSingleNodeTestCase; import java.util.Arrays; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import static java.util.Collections.emptyMap; @@ -55,8 +57,11 @@ public void testStartDeleteIndexEventCallback() throws Throwable { .setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0))); ensureGreen(); Index idx = resolveIndex("test"); - IndexMetaData metaData = indicesService.indexService(idx).getMetaData(); - ShardRouting shardRouting = indicesService.indexService(idx).getShard(0).routingEntry(); + IndexService indexService = indicesService.indexService(idx); + IndexMetaData metaData = indexService.getMetaData(); + ShardRouting shardRouting = indexService.getShard(0).routingEntry(); + Set globalBlocks = indexService.getIndexSettings().getIndexBlocks().global(); + Set indexBlocks = indexService.getIndexSettings().getIndexBlocks().indices().get(idx.getName()); final AtomicInteger counter = new AtomicInteger(1); IndexEventListener countingListener = new IndexEventListener() { @@ -121,7 +126,7 @@ public void afterIndexRemoved(Index index, IndexSettings indexSettings, IndexRem }; indicesService.removeIndex(idx, DELETED, "simon says"); try { - IndexService index = indicesService.createIndex(metaData, Arrays.asList(countingListener)); + IndexService index = indicesService.createIndex(metaData, globalBlocks, indexBlocks, Arrays.asList(countingListener)); assertEquals(3, counter.get()); idx = index.index(); ShardRouting newRouting = shardRouting; diff --git a/server/src/test/java/org/elasticsearch/indices/IndicesServiceTests.java b/server/src/test/java/org/elasticsearch/indices/IndicesServiceTests.java index cfac866895f0e..11e36b17f40fa 100644 --- a/server/src/test/java/org/elasticsearch/indices/IndicesServiceTests.java +++ b/server/src/test/java/org/elasticsearch/indices/IndicesServiceTests.java @@ -84,6 +84,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.Collections.emptyList; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.elasticsearch.cluster.shards.ClusterShardLimitIT.ShardCounts.forDataNodeCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; @@ -541,7 +542,7 @@ public void testGetEngineFactory() throws IOException { .numberOfShards(1) .numberOfReplicas(0) .build(); - final IndexService indexService = indicesService.createIndex(indexMetaData, Collections.emptyList()); + final IndexService indexService = indicesService.createIndex(indexMetaData, null, null, emptyList()); if (value != null && value) { assertThat(indexService.getEngineFactory(), instanceOf(FooEnginePlugin.FooEngineFactory.class)); } else { @@ -550,7 +551,7 @@ public void testGetEngineFactory() throws IOException { } } - public void testConflictingEngineFactories() throws IOException { + public void testConflictingEngineFactories() { final String indexName = "foobar"; final Index index = new Index(indexName, UUIDs.randomBase64UUID()); final Settings settings = Settings.builder() @@ -567,7 +568,7 @@ public void testConflictingEngineFactories() throws IOException { final IndicesService indicesService = getIndicesService(); final IllegalStateException e = - expectThrows(IllegalStateException.class, () -> indicesService.createIndex(indexMetaData, Collections.emptyList())); + expectThrows(IllegalStateException.class, () -> indicesService.createIndex(indexMetaData, null, null, emptyList())); final String pattern = ".*multiple engine factories provided for \\[foobar/.*\\]: \\[.*FooEngineFactory\\],\\[.*BarEngineFactory\\].*"; assertThat(e, hasToString(new RegexMatcher(pattern))); diff --git a/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java b/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java index c480c5719035d..2ae96d7fc2aa5 100644 --- a/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java +++ b/server/src/test/java/org/elasticsearch/indices/cluster/AbstractIndicesClusterStateServiceTestCase.java @@ -21,11 +21,13 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.IndexShardRoutingTable; import org.elasticsearch.cluster.routing.RoutingNode; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.Index; @@ -104,6 +106,7 @@ public void assertClusterStateMatchesNodeState(ClusterState state, IndicesCluste // check that all shards in local routing nodes have been allocated for (ShardRouting shardRouting : localRoutingNode) { Index index = shardRouting.index(); + String indexName = index.getName(); IndexMetaData indexMetaData = state.metaData().getIndexSafe(index); MockIndexShard shard = indicesService.getShardOrNull(shardRouting.shardId()); @@ -130,13 +133,14 @@ public void assertClusterStateMatchesNodeState(ClusterState state, IndicesCluste if (shard != null) { AllocatedIndex indexService = indicesService.indexService(index); - assertTrue("Index " + index + " expected but missing in indicesService", indexService != null); + assertNotNull("Index " + index + " expected but missing in indicesService", indexService); // index metadata has been updated - assertThat(indexService.getIndexSettings().getIndexMetaData(), equalTo(indexMetaData)); + final IndexSettings indexSettings = indexService.getIndexSettings(); + assertThat(indexSettings.getIndexMetaData(), equalTo(indexMetaData)); // shard has been created if (enableRandomFailures == false || failedShard == null) { - assertTrue("Shard with id " + shardRouting + " expected but missing in indexService", shard != null); + assertNotNull("Shard with id " + shardRouting + " expected but missing in indexService", shard); // shard has latest shard routing assertThat(shard.routingEntry(), equalTo(shardRouting)); } @@ -150,6 +154,10 @@ public void assertClusterStateMatchesNodeState(ClusterState state, IndicesCluste assertThat(shard.routingEntry() + " isn't updated with routing table", shard.routingTable, equalTo(shardRoutingTable)); } + assertThat("global level blocks should have been updated in IndexSettings for " + indexName, + indexSettings.getIndexBlocks().global(), equalTo(state.blocks().global())); + assertThat("index level blocks should have been updated in IndexSettings for " + indexName, + indexSettings.getIndexBlocks().indices().get(indexName), equalTo(state.blocks().indices().get(indexName))); } } } @@ -194,8 +202,11 @@ protected class MockIndicesService implements AllocatedIndices globalBlocks, + Set indexBlocks, List buildInIndexListener) throws IOException { - MockIndexService indexService = new MockIndexService(new IndexSettings(indexMetaData, Settings.EMPTY)); + MockIndexService indexService = new MockIndexService(new IndexSettings(indexMetaData, Settings.EMPTY, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, globalBlocks, indexBlocks)); indices = newMapBuilder(indices).put(indexMetaData.getIndexUUID(), indexService).immutableMap(); return indexService; } @@ -286,6 +297,11 @@ public void updateMetaData(final IndexMetaData currentIndexMetaData, final Index } } + @Override + public void updateBlocks(@Nullable final Set globalBlocks, @Nullable final Set indexBlocks) { + indexSettings.updateIndexBlocks(globalBlocks, indexBlocks); + } + @Override public MockIndexShard getShardOrNull(int shardId) { return shards.get(shardId); diff --git a/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java b/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java index d84bef7ed330a..2007b362944c1 100644 --- a/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java +++ b/server/src/test/java/org/elasticsearch/indices/cluster/ClusterStateChanges.java @@ -50,6 +50,7 @@ import org.elasticsearch.cluster.action.shard.ShardStateAction; import org.elasticsearch.cluster.action.shard.ShardStateAction.FailedShardEntry; import org.elasticsearch.cluster.action.shard.ShardStateAction.StartedShardEntry; +import org.elasticsearch.cluster.block.ClusterBlock; import org.elasticsearch.cluster.metadata.AliasValidator; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; @@ -101,6 +102,7 @@ import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anySetOf; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -151,7 +153,8 @@ public ClusterStateChanges(NamedXContentRegistry xContentRegistry, ThreadPool th // MetaDataCreateIndexService creates indices using its IndicesService instance to check mappings -> fake it here try { @SuppressWarnings("unchecked") final List listeners = anyList(); - when(indicesService.createIndex(any(IndexMetaData.class), listeners)) + when(indicesService + .createIndex(any(IndexMetaData.class), anySetOf(ClusterBlock.class), anySetOf(ClusterBlock.class), listeners)) .then(invocationOnMock -> { IndexService indexService = mock(IndexService.class); IndexMetaData indexMetaData = (IndexMetaData)invocationOnMock.getArguments()[0]; diff --git a/server/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java b/server/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java index 4625aa04be372..cc25c3a8cb550 100644 --- a/server/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java +++ b/server/src/test/java/org/elasticsearch/indices/cluster/IndicesClusterStateServiceRandomUpdatesTests.java @@ -33,6 +33,7 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.action.shard.ShardStateAction; import org.elasticsearch.cluster.block.ClusterBlock; +import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.block.ClusterBlocks; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; @@ -52,6 +53,7 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.recovery.PeerRecoveryTargetService; import org.elasticsearch.repositories.RepositoriesService; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.Transport; @@ -308,6 +310,20 @@ public ClusterState randomlyUpdateClusterState(ClusterState state, return state; } + final ClusterBlock globalTestBlock = new ClusterBlock(1, "test global block", false, false, false, + RestStatus.INTERNAL_SERVER_ERROR, ClusterBlockLevel.READ_WRITE); + + // randomly remove the global test block + if (randomBoolean() && state.blocks().hasGlobalBlock(globalTestBlock)) { + state = ClusterState.builder(state).blocks( + ClusterBlocks.builder().blocks(state.blocks()).removeGlobalBlock(globalTestBlock)).build(); + } + // randomly add global test block + if (rarely() && state.blocks().hasGlobalBlock(globalTestBlock) == false) { + state = ClusterState.builder(state).blocks( + ClusterBlocks.builder().blocks(state.blocks()).addGlobalBlock(globalTestBlock)).build(); + } + // randomly create new indices (until we have 200 max) for (int i = 0; i < randomInt(5); i++) { if (state.metaData().indices().size() > 200) { @@ -323,6 +339,18 @@ public ClusterState randomlyUpdateClusterState(ClusterState state, } else { settingsBuilder.put(SETTING_NUMBER_OF_REPLICAS, randomInt(2)); } + + // randomly add index blocks + if (rarely()) { + final ClusterBlocks.Builder blocks = ClusterBlocks.builder().blocks(state.blocks()); + Set indexBlocks = + new HashSet<>(randomSubsetOf(Arrays.asList(IndexMetaData.INDEX_READ_BLOCK, IndexMetaData.INDEX_WRITE_BLOCK))); + for (ClusterBlock indexBlock : indexBlocks) { + blocks.addIndexBlock(name, indexBlock); + } + state = ClusterState.builder(state).blocks(blocks).build(); + } + CreateIndexRequest request = new CreateIndexRequest(name, settingsBuilder.build()).waitForActiveShards(ActiveShardCount.NONE); state = cluster.createIndex(state, request); assertTrue(state.metaData().hasIndex(name));