diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f95cb2484984..332dad2a7370a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,9 +26,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- Add _list/shards API as paginated alternate to _cat/shards ([#14641](https://github.com/opensearch-project/OpenSearch/pull/14641))
- Latency and Memory allocation improvements to Multi Term Aggregation queries ([#14993](https://github.com/opensearch-project/OpenSearch/pull/14993))
- Flat object field use IndexOrDocValuesQuery to optimize query ([#14383](https://github.com/opensearch-project/OpenSearch/issues/14383))
+- Add support for renaming aliases during snapshot restore ([#16292](https://github.com/opensearch-project/OpenSearch/pull/16292))
- Add method to return dynamic SecureTransportParameters from SecureTransportSettingsProvider interface ([#16387](https://github.com/opensearch-project/OpenSearch/pull/16387))
- URI path filtering support in cluster stats API ([#15938](https://github.com/opensearch-project/OpenSearch/pull/15938))
-- [Star Tree - Search] Add support for metric aggregations with/without term query ([15289](https://github.com/opensearch-project/OpenSearch/pull/15289))
### Dependencies
- Bump `com.azure:azure-identity` from 1.13.0 to 1.13.2 ([#15578](https://github.com/opensearch-project/OpenSearch/pull/15578))
diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
index f3110cc8f20a5..42c64e04268e3 100644
--- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
+++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
@@ -112,6 +112,8 @@ private static StorageType fromString(String string) {
private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen();
private String renamePattern;
private String renameReplacement;
+ private String renameAliasPattern;
+ private String renameAliasReplacement;
private boolean waitForCompletion;
private boolean includeGlobalState = false;
private boolean partial = false;
@@ -164,6 +166,13 @@ public RestoreSnapshotRequest(StreamInput in) throws IOException {
if (in.getVersion().onOrAfter(Version.V_2_17_0)) {
sourceRemoteTranslogRepository = in.readOptionalString();
}
+ // TODO: change to V_2_18_0 once this is backported into that version
+ if (in.getVersion().onOrAfter(Version.CURRENT)) {
+ renameAliasPattern = in.readOptionalString();
+ }
+ if (in.getVersion().onOrAfter(Version.CURRENT)) {
+ renameAliasReplacement = in.readOptionalString();
+ }
}
@Override
@@ -191,6 +200,13 @@ public void writeTo(StreamOutput out) throws IOException {
if (out.getVersion().onOrAfter(Version.V_2_17_0)) {
out.writeOptionalString(sourceRemoteTranslogRepository);
}
+ // TODO: change to V_2_18_0 once this is backported into that version
+ if (out.getVersion().onOrAfter(Version.CURRENT)) {
+ out.writeOptionalString(renameAliasPattern);
+ }
+ if (out.getVersion().onOrAfter(Version.CURRENT)) {
+ out.writeOptionalString(renameAliasReplacement);
+ }
}
@Override
@@ -361,6 +377,51 @@ public String renameReplacement() {
return renameReplacement;
}
+ /**
+ * Sets rename pattern that should be applied to restored indices' alias.
+ *
+ * Alias that match the rename pattern will be renamed according to {@link #renameAliasReplacement(String)}. The
+ * rename pattern is applied according to the {@link java.util.regex.Matcher#appendReplacement(StringBuffer, String)}
+ * If two or more aliases are renamed into the same name, they will be merged.
+ *
+ * @param renameAliasPattern rename pattern
+ * @return this request
+ */
+ public RestoreSnapshotRequest renameAliasPattern(String renameAliasPattern) {
+ this.renameAliasPattern = renameAliasPattern;
+ return this;
+ }
+
+ /**
+ * Returns rename alias pattern
+ *
+ * @return rename alias pattern
+ */
+ public String renameAliasPattern() {
+ return renameAliasPattern;
+ }
+
+ /**
+ * Sets rename alias replacement
+ *
+ * See {@link #renameAliasPattern(String)} for more information.
+ *
+ * @param renameAliasReplacement rename replacement
+ */
+ public RestoreSnapshotRequest renameAliasReplacement(String renameAliasReplacement) {
+ this.renameAliasReplacement = renameAliasReplacement;
+ return this;
+ }
+
+ /**
+ * Returns rename alias replacement
+ *
+ * @return rename alias replacement
+ */
+ public String renameAliasReplacement() {
+ return renameAliasReplacement;
+ }
+
/**
* If this parameter is set to true the operation will wait for completion of restore process before returning.
*
@@ -625,6 +686,18 @@ public RestoreSnapshotRequest source(Map source) {
} else {
throw new IllegalArgumentException("malformed rename_replacement");
}
+ } else if (name.equals("rename_alias_pattern")) {
+ if (entry.getValue() instanceof String) {
+ renameAliasPattern((String) entry.getValue());
+ } else {
+ throw new IllegalArgumentException("malformed rename_alias_pattern");
+ }
+ } else if (name.equals("rename_alias_replacement")) {
+ if (entry.getValue() instanceof String) {
+ renameAliasReplacement((String) entry.getValue());
+ } else {
+ throw new IllegalArgumentException("malformed rename_alias_replacement");
+ }
} else if (name.equals("index_settings")) {
if (!(entry.getValue() instanceof Map)) {
throw new IllegalArgumentException("malformed index_settings section");
@@ -685,6 +758,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
if (renameReplacement != null) {
builder.field("rename_replacement", renameReplacement);
}
+ if (renameAliasPattern != null) {
+ builder.field("rename_alias_pattern", renameAliasPattern);
+ }
+ if (renameAliasReplacement != null) {
+ builder.field("rename_alias_replacement", renameAliasReplacement);
+ }
builder.field("include_global_state", includeGlobalState);
builder.field("partial", partial);
builder.field("include_aliases", includeAliases);
@@ -733,6 +812,8 @@ public boolean equals(Object o) {
&& Objects.equals(indicesOptions, that.indicesOptions)
&& Objects.equals(renamePattern, that.renamePattern)
&& Objects.equals(renameReplacement, that.renameReplacement)
+ && Objects.equals(renameAliasPattern, that.renameAliasPattern)
+ && Objects.equals(renameAliasReplacement, that.renameAliasReplacement)
&& Objects.equals(indexSettings, that.indexSettings)
&& Arrays.equals(ignoreIndexSettings, that.ignoreIndexSettings)
&& Objects.equals(snapshotUuid, that.snapshotUuid)
@@ -751,6 +832,8 @@ public int hashCode() {
indicesOptions,
renamePattern,
renameReplacement,
+ renameAliasPattern,
+ renameAliasReplacement,
waitForCompletion,
includeGlobalState,
partial,
diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java
index 53c9557a621b7..038d62ad7f4cb 100644
--- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java
+++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java
@@ -144,6 +144,34 @@ public RestoreSnapshotRequestBuilder setRenameReplacement(String renameReplaceme
return this;
}
+ /**
+ * Sets rename pattern that should be applied to restored indices' aliases.
+ *
+ * Aliases that match the rename pattern will be renamed according to {@link #setRenameAliasReplacement(String)}. The
+ * rename pattern is applied according to the {@link java.util.regex.Matcher#appendReplacement(StringBuffer, String)}
+ * The request will fail if two or more alias will be renamed into the same name.
+ *
+ * @param renameAliasPattern rename alias pattern
+ * @return this builder
+ */
+ public RestoreSnapshotRequestBuilder setRenameAliasPattern(String renameAliasPattern) {
+ request.renameAliasPattern(renameAliasPattern);
+ return this;
+ }
+
+ /**
+ * Sets rename replacement
+ *
+ * See {@link #setRenameAliasPattern(String)} for more information.
+ *
+ * @param renameAliasReplacement rename alias replacement
+ * @return this builder
+ */
+ public RestoreSnapshotRequestBuilder setRenameAliasReplacement(String renameAliasReplacement) {
+ request.renameAliasReplacement(renameAliasReplacement);
+ return this;
+ }
+
/**
* If this parameter is set to true the operation will wait for completion of restore process before returning.
*
diff --git a/server/src/main/java/org/opensearch/snapshots/RestoreService.java b/server/src/main/java/org/opensearch/snapshots/RestoreService.java
index 79a70d835f773..88eff93e51b38 100644
--- a/server/src/main/java/org/opensearch/snapshots/RestoreService.java
+++ b/server/src/main/java/org/opensearch/snapshots/RestoreService.java
@@ -111,6 +111,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.Collections.unmodifiableSet;
@@ -486,9 +487,7 @@ public ClusterState execute(ClusterState currentState) {
// Remove all aliases - they shouldn't be restored
indexMdBuilder.removeAllAliases();
} else {
- for (final String alias : snapshotIndexMetadata.getAliases().keySet()) {
- aliases.add(alias);
- }
+ applyAliasesWithRename(snapshotIndexMetadata, indexMdBuilder, aliases);
}
IndexMetadata updatedIndexMetadata = indexMdBuilder.build();
if (partial) {
@@ -533,9 +532,7 @@ public ClusterState execute(ClusterState currentState) {
indexMdBuilder.putAlias(alias);
}
} else {
- for (final String alias : snapshotIndexMetadata.getAliases().keySet()) {
- aliases.add(alias);
- }
+ applyAliasesWithRename(snapshotIndexMetadata, indexMdBuilder, aliases);
}
final Settings.Builder indexSettingsBuilder = Settings.builder()
.put(snapshotIndexMetadata.getSettings())
@@ -665,6 +662,27 @@ private void checkAliasNameConflicts(Map renamedIndices, Set aliases
+ ) {
+ if (request.renameAliasPattern() == null || request.renameAliasReplacement() == null) {
+ aliases.addAll(snapshotIndexMetadata.getAliases().keySet());
+ } else {
+ Pattern renameAliasPattern = Pattern.compile(request.renameAliasPattern());
+ for (final Map.Entry alias : snapshotIndexMetadata.getAliases().entrySet()) {
+ String currentAliasName = alias.getKey();
+ indexMdBuilder.removeAlias(currentAliasName);
+ String newAliasName = renameAliasPattern.matcher(currentAliasName)
+ .replaceAll(request.renameAliasReplacement());
+ AliasMetadata newAlias = AliasMetadata.newAliasMetadata(alias.getValue(), newAliasName);
+ indexMdBuilder.putAlias(newAlias);
+ aliases.add(newAliasName);
+ }
+ }
+ }
+
private String[] getIgnoreSettingsInternal() {
// for non-remote store enabled domain, we will remove all the remote store
// related index settings present in the snapshot.
diff --git a/server/src/test/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java b/server/src/test/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
index c3de3413edd13..04cc45f3477c6 100644
--- a/server/src/test/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
+++ b/server/src/test/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestTests.java
@@ -71,6 +71,12 @@ private RestoreSnapshotRequest randomState(RestoreSnapshotRequest instance) {
if (randomBoolean()) {
instance.renameReplacement(randomUnicodeOfLengthBetween(1, 100));
}
+ if (randomBoolean()) {
+ instance.renameAliasPattern(randomUnicodeOfLengthBetween(1, 100));
+ }
+ if (randomBoolean()) {
+ instance.renameAliasReplacement(randomUnicodeOfLengthBetween(1, 100));
+ }
instance.partial(randomBoolean());
instance.includeAliases(randomBoolean());
diff --git a/server/src/test/java/org/opensearch/snapshots/RestoreServiceIntegTests.java b/server/src/test/java/org/opensearch/snapshots/RestoreServiceIntegTests.java
new file mode 100644
index 0000000000000..92da980d70f34
--- /dev/null
+++ b/server/src/test/java/org/opensearch/snapshots/RestoreServiceIntegTests.java
@@ -0,0 +1,297 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.snapshots;
+
+import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+
+import org.opensearch.action.StepListener;
+import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse;
+import org.opensearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
+import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
+import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse;
+import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
+import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
+import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest;
+import org.opensearch.action.admin.indices.close.CloseIndexRequest;
+import org.opensearch.action.admin.indices.close.CloseIndexResponse;
+import org.opensearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequest;
+import org.opensearch.action.admin.indices.exists.indices.IndicesExistsResponse;
+import org.opensearch.action.admin.indices.open.OpenIndexRequest;
+import org.opensearch.action.admin.indices.open.OpenIndexResponse;
+import org.opensearch.action.bulk.BulkRequest;
+import org.opensearch.action.bulk.BulkResponse;
+import org.opensearch.action.index.IndexRequest;
+import org.opensearch.action.search.SearchRequest;
+import org.opensearch.action.search.SearchResponse;
+import org.opensearch.action.support.WriteRequest;
+import org.opensearch.action.support.master.AcknowledgedResponse;
+import org.opensearch.common.CheckedConsumer;
+import org.opensearch.common.settings.Settings;
+import org.opensearch.repositories.fs.FsRepository;
+import org.opensearch.search.builder.SearchSourceBuilder;
+import org.opensearch.test.OpenSearchIntegTestCase;
+import org.opensearch.test.OpenSearchSingleNodeTestCase;
+import org.junit.After;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class RestoreServiceIntegTests extends OpenSearchSingleNodeTestCase {
+ private final String indexName = "index_1";
+ private final String renamedIndexName = "index_2";
+ private final String aliasName = "alias_1";
+ private final String renamedAliasName = "alias_2";
+ private final String repoName = "repo_1";
+ private final String snapShotName = "snap_1";
+ private final int waitInSeconds = 60;
+ private boolean exists;
+ private boolean closed;
+ private boolean includeAlias;
+ private boolean renameAliases;
+ private boolean renameIndexes;
+
+ public RestoreServiceIntegTests(TestCase testCase) {
+ this.exists = testCase.exists;
+ this.closed = testCase.closed;
+ this.includeAlias = testCase.includeAlias;
+ this.renameAliases = testCase.renameAliases;
+ this.renameIndexes = testCase.renameIndexes;
+ }
+
+ public static class TestCase {
+ public boolean exists;
+ public boolean closed;
+ public boolean includeAlias;
+ public boolean renameAliases;
+ public boolean renameIndexes;
+
+ public TestCase(boolean exists, boolean closed, boolean includeAlias, boolean renameAliases, boolean renameIndexes) {
+ this.exists = exists;
+ this.closed = closed;
+ this.includeAlias = includeAlias;
+ this.renameAliases = renameAliases;
+ this.renameIndexes = renameIndexes;
+ }
+
+ public String toString() {
+ return String.join(
+ " and ",
+ new String[] {
+ exists ? "target index exists and is" + (closed ? "closed" : "open") : "doesn't exist",
+ includeAlias ? "including aliases" : "not including aliases",
+ renameIndexes ? "renaming indexes" : "not renaming indexes",
+ renameAliases ? "renaming aliases" : "not renaming aliases" }
+ );
+ }
+ }
+
+ @ParametersFactory
+ public static Collection