From 8f75f4fa7b58600262a3a14b7387af870af6702c Mon Sep 17 00:00:00 2001 From: AnnTian Shao Date: Sat, 25 Jan 2025 15:21:37 -0800 Subject: [PATCH] Add UnmodifiableOnRestore property to index.knn setting Signed-off-by: AnnTian Shao --- .../org/opensearch/knn/index/KNNSettings.java | 9 +- .../knn/index/RestoreSnapshotIT.java | 122 ++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/opensearch/knn/index/RestoreSnapshotIT.java diff --git a/src/main/java/org/opensearch/knn/index/KNNSettings.java b/src/main/java/org/opensearch/knn/index/KNNSettings.java index 097329d81..abce6cc76 100644 --- a/src/main/java/org/opensearch/knn/index/KNNSettings.java +++ b/src/main/java/org/opensearch/knn/index/KNNSettings.java @@ -44,6 +44,7 @@ import static org.opensearch.common.settings.Setting.Property.IndexScope; import static org.opensearch.common.settings.Setting.Property.NodeScope; import static org.opensearch.common.settings.Setting.Property.Final; +import static org.opensearch.common.settings.Setting.Property.UnmodifiableOnRestore; import static org.opensearch.common.unit.MemorySizeValue.parseBytesSizeValueOrHeapRatio; import static org.opensearch.core.common.unit.ByteSizeValue.parseBytesSizeValue; import static org.opensearch.knn.common.featureflags.KNNFeatureFlags.getFeatureFlags; @@ -269,7 +270,13 @@ public class KNNSettings { /** * This setting identifies KNN index. */ - public static final Setting IS_KNN_INDEX_SETTING = Setting.boolSetting(KNN_INDEX, false, IndexScope, Final); + public static final Setting IS_KNN_INDEX_SETTING = Setting.boolSetting( + KNN_INDEX, + false, + IndexScope, + Final, + UnmodifiableOnRestore + ); /** * index_thread_quantity - the parameter specifies how many threads the nms library should use to create the graph. diff --git a/src/test/java/org/opensearch/knn/index/RestoreSnapshotIT.java b/src/test/java/org/opensearch/knn/index/RestoreSnapshotIT.java new file mode 100644 index 000000000..36761f136 --- /dev/null +++ b/src/test/java/org/opensearch/knn/index/RestoreSnapshotIT.java @@ -0,0 +1,122 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.knn.index; + +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.ResponseException; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.index.IndexSettings; +import org.opensearch.test.rest.OpenSearchRestTestCase; + +import java.util.List; +import java.util.Map; + +import static org.hamcrest.Matchers.*; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; + +public class RestoreSnapshotIT extends OpenSearchRestTestCase { + + private String index; + private String snapshot; + + public void setupSnapshotRestore() throws Exception { + Request clusterSettingsRequest = new Request("GET", "/_cluster/settings"); + clusterSettingsRequest.addParameter("include_defaults", "true"); + Response clusterSettingsResponse = client().performRequest(clusterSettingsRequest); + Map clusterSettings = entityAsMap(clusterSettingsResponse); + + @SuppressWarnings("unchecked") + List pathRepos = (List) XContentMapValues.extractValue("defaults.path.repo", clusterSettings); + assertThat(pathRepos, notNullValue()); + assertThat(pathRepos, hasSize(1)); + + final String pathRepo = pathRepos.get(0); + + index = "test-index"; + snapshot = "snapshot-" + index; + + // create index + XContentBuilder settings = jsonBuilder(); + settings.startObject(); + { + settings.startObject("settings"); + settings.field(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1); + settings.field(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1); + settings.field(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true); + settings.endObject(); + } + settings.endObject(); + + Request createIndex = new Request("PUT", "/" + index); + createIndex.setJsonEntity(settings.toString()); + createIndex.setOptions(allowTypesRemovalWarnings()); + client().performRequest(createIndex); + + // create repo + XContentBuilder repoConfig = JsonXContent.contentBuilder().startObject(); + { + repoConfig.field("type", "fs"); + repoConfig.startObject("settings"); + { + repoConfig.field("compress", randomBoolean()); + repoConfig.field("location", pathRepo); + } + repoConfig.endObject(); + } + repoConfig.endObject(); + Request createRepoRequest = new Request("PUT", "/_snapshot/repo"); + createRepoRequest.setJsonEntity(repoConfig.toString()); + client().performRequest(createRepoRequest); + + // create snapshot + Request createSnapshot = new Request("PUT", "/_snapshot/repo/" + snapshot); + createSnapshot.addParameter("wait_for_completion", "true"); + createSnapshot.setJsonEntity("{\"indices\": \"" + index + "\"}"); + client().performRequest(createSnapshot); + } + + public void testUnmodifiableOnRestoreSettingModifiedOnRestore() throws Exception { + setupSnapshotRestore(); + + // invalid restore + XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject(); + restoreCommand.field("indices", index); + restoreCommand.field("rename_pattern", index); + restoreCommand.field("rename_replacement", "restored-" + index); + restoreCommand.startObject("index_settings"); + { + restoreCommand.field("index.knn", false); + } + restoreCommand.endObject(); + restoreCommand.endObject(); + Request restoreRequest = new Request("POST", "/_snapshot/repo/" + snapshot + "/_restore"); + restoreRequest.addParameter("wait_for_completion", "true"); + restoreRequest.setJsonEntity(restoreCommand.toString()); + final ResponseException error = expectThrows(ResponseException.class, () -> client().performRequest(restoreRequest)); + assertThat(error.getMessage(), containsString("cannot modify UnmodifiableOnRestore setting [index.knn]" + " on restore")); + } + + public void testUnmodifiableOnRestoreSettingIgnoredOnRestore() throws Exception { + setupSnapshotRestore(); + + // invalid restore + XContentBuilder restoreCommand = JsonXContent.contentBuilder().startObject(); + restoreCommand.field("indices", index); + restoreCommand.field("rename_pattern", index); + restoreCommand.field("rename_replacement", "restored-" + index); + restoreCommand.field("ignore_index_settings", "index.knn"); + restoreCommand.endObject(); + Request restoreRequest = new Request("POST", "/_snapshot/repo/" + snapshot + "/_restore"); + restoreRequest.addParameter("wait_for_completion", "true"); + restoreRequest.setJsonEntity(restoreCommand.toString()); + final ResponseException error = expectThrows(ResponseException.class, () -> client().performRequest(restoreRequest)); + assertThat(error.getMessage(), containsString("cannot remove UnmodifiableOnRestore setting [index.knn] on restore")); + } +}