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 6202ee0cd3690..a8a114bfaa44e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataCreateIndexService.java @@ -105,6 +105,8 @@ import static org.elasticsearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; import static org.elasticsearch.cluster.metadata.MetadataCreateDataStreamService.validateTimestampFieldMapping; import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.resolveSettings; +import static org.elasticsearch.index.IndexModule.INDEX_RECOVERY_TYPE_SETTING; +import static org.elasticsearch.index.IndexModule.INDEX_STORE_TYPE_SETTING; /** * Service responsible for submitting create index requests @@ -1140,6 +1142,9 @@ private static List validateIndexCustomPath(Settings settings, @Nullable */ static List validateShrinkIndex(ClusterState state, String sourceIndex, String targetIndexName, Settings targetIndexSettings) { IndexMetadata sourceMetadata = validateResize(state, sourceIndex, targetIndexName, targetIndexSettings); + if ("snapshot".equals(INDEX_STORE_TYPE_SETTING.get(sourceMetadata.getSettings()))) { + throw new IllegalArgumentException("can't shrink searchable snapshot index [" + sourceIndex + ']'); + } assert INDEX_NUMBER_OF_SHARDS_SETTING.exists(targetIndexSettings); IndexMetadata.selectShrinkShards(0, sourceMetadata, INDEX_NUMBER_OF_SHARDS_SETTING.get(targetIndexSettings)); @@ -1171,6 +1176,9 @@ static List validateShrinkIndex(ClusterState state, String sourceIndex, static void validateSplitIndex(ClusterState state, String sourceIndex, String targetIndexName, Settings targetIndexSettings) { IndexMetadata sourceMetadata = validateResize(state, sourceIndex, targetIndexName, targetIndexSettings); + if ("snapshot".equals(INDEX_STORE_TYPE_SETTING.get(sourceMetadata.getSettings()))) { + throw new IllegalArgumentException("can't split searchable snapshot index [" + sourceIndex + ']'); + } IndexMetadata.selectSplitShard(0, sourceMetadata, IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.get(targetIndexSettings)); if (sourceMetadata.getCreationVersion().before(Version.V_6_0_0_alpha1)) { // ensure we have a single type since this would make the splitting code considerably more complex @@ -1182,6 +1190,15 @@ static void validateSplitIndex(ClusterState state, String sourceIndex, String ta static void validateCloneIndex(ClusterState state, String sourceIndex, String targetIndexName, Settings targetIndexSettings) { IndexMetadata sourceMetadata = validateResize(state, sourceIndex, targetIndexName, targetIndexSettings); + if ("snapshot".equals(INDEX_STORE_TYPE_SETTING.get(sourceMetadata.getSettings()))) { + for (Setting nonCloneableSetting : Arrays.asList(INDEX_STORE_TYPE_SETTING, INDEX_RECOVERY_TYPE_SETTING)) { + if (nonCloneableSetting.exists(targetIndexSettings) == false) { + throw new IllegalArgumentException("can't clone searchable snapshot index [" + sourceIndex + "]; setting [" + + nonCloneableSetting.getKey() + + "] should be overridden"); + } + } + } IndexMetadata.selectCloneShard(0, sourceMetadata, INDEX_NUMBER_OF_SHARDS_SETTING.get(targetIndexSettings)); } @@ -1201,7 +1218,6 @@ static IndexMetadata validateResize(ClusterState state, String sourceIndex, Stri throw new IllegalArgumentException(String.format(Locale.ROOT, "cannot resize the write index [%s] for data stream [%s]", sourceIndex, source.getParentDataStream().getName())); } - // ensure index is read-only if (state.blocks().indexBlocked(ClusterBlockLevel.WRITE, sourceIndex) == false) { throw new IllegalStateException("index " + sourceIndex + " must be read-only to resize index. use \"index.blocks.write=true\""); diff --git a/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsResizeIntegTests.java b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsResizeIntegTests.java new file mode 100644 index 0000000000000..907517b35796a --- /dev/null +++ b/x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsResizeIntegTests.java @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.searchablesnapshots; + +import org.elasticsearch.action.admin.indices.shrink.ResizeType; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.IndexModule; +import org.elasticsearch.repositories.fs.FsRepository; +import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.xpack.cluster.routing.allocation.DataTierAllocationDecider; +import org.elasticsearch.xpack.core.DataTier; +import org.junit.After; +import org.junit.Before; + +import static java.util.Collections.singletonList; +import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING; +import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING; +import static org.elasticsearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING; +import static org.elasticsearch.index.IndexSettings.INDEX_SOFT_DELETES_SETTING; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.elasticsearch.xpack.core.searchablesnapshots.MountSearchableSnapshotRequest.Storage; +import static org.hamcrest.Matchers.equalTo; + +@ESIntegTestCase.ClusterScope(numDataNodes = 1) +public class SearchableSnapshotsResizeIntegTests extends BaseFrozenSearchableSnapshotsIntegTestCase { + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + createRepository("repository", FsRepository.TYPE); + assertAcked( + prepareCreate( + "index", + Settings.builder() + .put(INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 2) + .put(INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.getKey(), 4) + .put(INDEX_SOFT_DELETES_SETTING.getKey(), true) + ) + ); + indexRandomDocs("index", scaledRandomIntBetween(0, 1_000)); + createSnapshot("repository", "snapshot", singletonList("index")); + assertAcked(client().admin().indices().prepareDelete("index")); + mountSnapshot("repository", "snapshot", "index", "mounted-index", Settings.EMPTY, randomFrom(Storage.values())); + ensureGreen("mounted-index"); + } + + @After + @Override + public void tearDown() throws Exception { + assertAcked(client().admin().indices().prepareDelete("mounted-*")); + assertAcked(client().admin().cluster().prepareDeleteSnapshot("repository", "snapshot").get()); + assertAcked(client().admin().cluster().prepareDeleteRepository("repository")); + super.tearDown(); + } + + public void testShrinkSearchableSnapshotIndex() { + final IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareResizeIndex("mounted-index", "shrunk-index") + .setResizeType(ResizeType.SHRINK) + .setSettings(indexSettingsNoReplicas(1).build()) + .get() + ); + assertThat(exception.getMessage(), equalTo("can't shrink searchable snapshot index [mounted-index]")); + } + + public void testSplitSearchableSnapshotIndex() { + final IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareResizeIndex("mounted-index", "split-index") + .setResizeType(ResizeType.SPLIT) + .setSettings(indexSettingsNoReplicas(4).build()) + .get() + ); + assertThat(exception.getMessage(), equalTo("can't split searchable snapshot index [mounted-index]")); + } + + public void testCloneSearchableSnapshotIndex() { + IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareResizeIndex("mounted-index", "cloned-index").setResizeType(ResizeType.CLONE).get() + ); + assertThat( + exception.getMessage(), + equalTo("can't clone searchable snapshot index [mounted-index]; setting [index.store.type] should be overridden") + ); + + exception = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareResizeIndex("mounted-index", "cloned-index") + .setResizeType(ResizeType.CLONE) + .setSettings(Settings.builder().putNull(IndexModule.INDEX_STORE_TYPE_SETTING.getKey()).build()) + .get() + ); + assertThat( + exception.getMessage(), + equalTo("can't clone searchable snapshot index [mounted-index]; setting [index.recovery.type] should be overridden") + ); + + assertAcked( + client().admin() + .indices() + .prepareResizeIndex("mounted-index", "cloned-index") + .setResizeType(ResizeType.CLONE) + .setSettings( + Settings.builder() + .putNull(IndexModule.INDEX_STORE_TYPE_SETTING.getKey()) + .putNull(IndexModule.INDEX_RECOVERY_TYPE_SETTING.getKey()) + .put(DataTierAllocationDecider.INDEX_ROUTING_PREFER, DataTier.DATA_HOT) + .put(INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .build() + ) + ); + ensureGreen("cloned-index"); + assertAcked(client().admin().indices().prepareDelete("cloned-index")); + } +}