From af5fa3f649b53d7acc318dcfbcfa05866f2cd630 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Tue, 30 Nov 2021 11:49:21 +0100 Subject: [PATCH] [7.16] Fix data stream alias validation. (#81040) (#81136) * Fix data stream alias validation. (#81040) In case of restoring a snapshot, it is possible to overwrite an existing data stream with a data stream alias from a snapshot. This change fixes this by improving the generic duplicate name validation. On top of this the lack of data stream alias validation in Metadata.Builder#build() method resulted in cases where data stream aliases could be added for existing index aliases, data streams or indices with the same name. Closes #80972 * adjust to 7.16 reality * Unmute DataStreamsSnapshotsIT#testRestoreDataStreamAliasWithConflictingIndicesAlias() test and fix the test problem, which is that testRestoreDataStreamAliasWithConflictingDataStream() test needs to remove the composable index template that it adds. The base test class doesn't remove any composable index templates and this template interferes with the testRestoreDataStreamAliasWithConflictingIndicesAlias() test. Relates to #81040 --- .../cluster/metadata/Metadata.java | 19 +- .../cluster/metadata/MetadataTests.java | 85 +++++++- .../datastreams/DataStreamIT.java | 205 +++++++++++++++++- .../datastreams/DataStreamsSnapshotsIT.java | 39 ++++ 4 files changed, 340 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java index 2f07e7177fdd5..2e82ff55c2440 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java @@ -1740,17 +1740,23 @@ public Metadata build(boolean builtIndicesLookupEagerly) { indexMetadata.getAliases().keysIt().forEachRemaining(allAliases::add); } + final ArrayList duplicates = new ArrayList<>(); final Set allDataStreams = new HashSet<>(); DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE); if (dataStreamMetadata != null) { for (DataStream dataStream : dataStreamMetadata.dataStreams().values()) { allDataStreams.add(dataStream.getName()); } + // Adding data stream aliases: + for (String dataStreamAlias : dataStreamMetadata.getDataStreamAliases().keySet()) { + if (allAliases.add(dataStreamAlias) == false) { + duplicates.add("data stream alias and indices alias have the same name (" + dataStreamAlias + ")"); + } + } } final Set aliasDuplicatesWithIndices = new HashSet<>(allAliases); aliasDuplicatesWithIndices.retainAll(allIndices); - ArrayList duplicates = new ArrayList<>(); if (aliasDuplicatesWithIndices.isEmpty() == false) { // iterate again and constructs a helpful message for (ObjectCursor cursor : indices.values()) { @@ -1766,12 +1772,19 @@ public Metadata build(boolean builtIndicesLookupEagerly) { aliasDuplicatesWithDataStreams.retainAll(allDataStreams); if (aliasDuplicatesWithDataStreams.isEmpty() == false) { // iterate again and constructs a helpful message - for (ObjectCursor cursor : indices.values()) { - for (String alias : aliasDuplicatesWithDataStreams) { + for (String alias : aliasDuplicatesWithDataStreams) { + // reported var avoids adding a message twice if an index alias has the same name as a data stream. + boolean reported = false; + for (ObjectCursor cursor : indices.values()) { if (cursor.value.getAliases().containsKey(alias)) { duplicates.add(alias + " (alias of " + cursor.value.getIndex() + ") conflicts with data stream"); + reported = true; } } + // This is for adding an error message for when a data steam alias has the same name as a data stream. + if (reported == false && dataStreamMetadata != null && dataStreamMetadata.dataStreams().containsKey(alias)) { + duplicates.add("data stream alias and data stream have the same name (" + alias + ")"); + } } } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java index 3c808fe9ed5f5..84ff504b58757 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataTests.java @@ -48,6 +48,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import static java.util.Collections.singletonList; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createBackingIndex; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createFirstBackingIndex; import static org.elasticsearch.cluster.metadata.DataStreamTestHelper.createTimestampField; @@ -1231,9 +1232,7 @@ public void testOverlappingDataStreamNamesWithBackingIndexDatePattern() { .numberOfReplicas(1) .build(); b.put(ds2Index1, false); - b.put( - new DataStream(dataStreamName2, createTimestampField("@timestamp"), Collections.singletonList(ds2Index1.getIndex()), 1, null) - ); + b.put(new DataStream(dataStreamName2, createTimestampField("@timestamp"), singletonList(ds2Index1.getIndex()), 1, null)); Metadata metadata = b.build(); assertThat(metadata.dataStreams().size(), equalTo(2)); @@ -1314,6 +1313,74 @@ public void testBuildIndicesLookupForDataStreamAliases() { assertThat(value.getAliases(), nullValue()); } + public void testDataStreamAliasValidation() { + Metadata.Builder b = Metadata.builder(); + addDataStream("my-alias", b); + b.put("my-alias", "my-alias", null, null); + Exception e = expectThrows(IllegalStateException.class, b::build); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + + b = Metadata.builder(); + addDataStream("d1", b); + addDataStream("my-alias", b); + b.put("my-alias", "d1", null, null); + e = expectThrows(IllegalStateException.class, b::build); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + + b = Metadata.builder(); + b.put( + IndexMetadata.builder("index1") + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + ) + .putAlias(new AliasMetadata.Builder("my-alias")) + ); + + addDataStream("d1", b); + b.put("my-alias", "d1", null, null); + e = expectThrows(IllegalStateException.class, b::build); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (my-alias)")); + } + + public void testDataStreamAliasValidationRestoreScenario() { + Metadata.Builder b = Metadata.builder(); + b.dataStreams( + org.elasticsearch.core.Map.of("my-alias", createDataStream("my-alias")), + org.elasticsearch.core.Map.of("my-alias", new DataStreamAlias("my-alias", singletonList("my-alias"), null, null)) + ); + Exception e = expectThrows(IllegalStateException.class, b::build); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + + b = Metadata.builder(); + b.dataStreams( + org.elasticsearch.core.Map.of("d1", createDataStream("d1"), "my-alias", createDataStream("my-alias")), + org.elasticsearch.core.Map.of("my-alias", new DataStreamAlias("my-alias", singletonList("d1"), null, null)) + ); + e = expectThrows(IllegalStateException.class, b::build); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + + b = Metadata.builder(); + b.put( + IndexMetadata.builder("index1") + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + ) + .putAlias(new AliasMetadata.Builder("my-alias")) + ); + b.dataStreams( + org.elasticsearch.core.Map.of("d1", createDataStream("d1")), + org.elasticsearch.core.Map.of("my-alias", new DataStreamAlias("my-alias", singletonList("d1"), null, null)) + ); + e = expectThrows(IllegalStateException.class, b::build); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (my-alias)")); + } + private void addDataStream(String name, Metadata.Builder b) { int numBackingIndices = randomIntBetween(1, 4); List indices = new ArrayList<>(numBackingIndices); @@ -1325,6 +1392,16 @@ private void addDataStream(String name, Metadata.Builder b) { b.put(new DataStream(name, createTimestampField("@timestamp"), indices)); } + private DataStream createDataStream(String name) { + int numBackingIndices = randomIntBetween(1, 4); + List indices = new ArrayList<>(numBackingIndices); + for (int j = 1; j <= numBackingIndices; j++) { + IndexMetadata idx = createBackingIndex(name, j).build(); + indices.add(idx.getIndex()); + } + return new DataStream(name, createTimestampField("@timestamp"), indices); + } + public void testIndicesLookupRecordsDataStreamForBackingIndices() { final int numIndices = randomIntBetween(2, 5); final int numBackingIndices = randomIntBetween(2, 5); @@ -1772,7 +1849,7 @@ public void testReuseIndicesLookup() { DataStream dataStream = new DataStream( dataStreamName, new DataStream.TimestampField("@timestamp"), - Collections.singletonList(idx.getIndex()) + singletonList(idx.getIndex()) ); builder.put(dataStream); Metadata metadata = builder.build(); diff --git a/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java b/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java index d68e60d1bbaed..0e1c763fe757e 100644 --- a/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java +++ b/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java @@ -13,10 +13,12 @@ import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.get.GetIndexRequest; import org.elasticsearch.action.admin.indices.get.GetIndexResponse; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; @@ -59,6 +61,7 @@ import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.indices.InvalidAliasNameException; +import org.elasticsearch.indices.InvalidIndexNameException; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; @@ -1496,6 +1499,195 @@ public void testSegmentsSortedOnTimestampDesc() throws Exception { assertTrue(timestamp2 > timestamp3); } + public void testCreateDataStreamWithSameNameAsIndexAlias() throws Exception { + CreateIndexRequest createIndexRequest = new CreateIndexRequest("my-index").alias(new Alias("my-alias")); + assertAcked(client().admin().indices().create(createIndexRequest).actionGet()); + + // Important detail: create template with data stream template after the index has been created + DataStreamIT.putComposableIndexTemplate("my-template", List.of("my-*")); + + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("my-alias"); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().execute(CreateDataStreamAction.INSTANCE, request).actionGet() + ); + assertThat(e.getMessage(), containsString("[my-alias (alias of [")); + assertThat(e.getMessage(), containsString("]) conflicts with data stream")); + } + + public void testCreateDataStreamWithSameNameAsIndex() throws Exception { + CreateIndexRequest createIndexRequest = new CreateIndexRequest("my-index").alias(new Alias("my-alias")); + assertAcked(client().admin().indices().create(createIndexRequest).actionGet()); + + // Important detail: create template with data stream template after the index has been created + DataStreamIT.putComposableIndexTemplate("my-template", List.of("my-*")); + + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("my-index"); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().execute(CreateDataStreamAction.INSTANCE, request).actionGet() + ); + assertThat(e.getMessage(), containsString("data stream [my-index] conflicts with index")); + } + + public void testCreateDataStreamWithSameNameAsDataStreamAlias() throws Exception { + { + DataStreamIT.putComposableIndexTemplate("my-template", List.of("my-*")); + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("my-ds"); + assertAcked(client().execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest(); + aliasesAddRequest.addAliasAction(new AliasActions(AliasActions.Type.ADD).index("my-ds").aliases("my-alias")); + assertAcked(client().admin().indices().aliases(aliasesAddRequest).actionGet()); + + CreateDataStreamAction.Request request2 = new CreateDataStreamAction.Request("my-alias"); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().execute(CreateDataStreamAction.INSTANCE, request2).actionGet() + ); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + } + { + assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request("*")).actionGet()); + DataStreamIT.putComposableIndexTemplate( + "my-template", + null, + List.of("my-*"), + null, + null, + Map.of("my-alias", AliasMetadata.builder("my-alias").build()) + ); + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("my-ds"); + assertAcked(client().execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + + CreateDataStreamAction.Request request2 = new CreateDataStreamAction.Request("my-alias"); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().execute(CreateDataStreamAction.INSTANCE, request2).actionGet() + ); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + } + } + + public void testCreateDataStreamAliasWithSameNameAsIndexAlias() throws Exception { + { + DataStreamIT.putComposableIndexTemplate("my-template", List.of("logs-*")); + CreateIndexRequest createIndexRequest = new CreateIndexRequest("es-logs").alias(new Alias("logs")); + assertAcked(client().admin().indices().create(createIndexRequest).actionGet()); + + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("logs-es"); + assertAcked(client().execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest(); + aliasesAddRequest.addAliasAction(new AliasActions(AliasActions.Type.ADD).index("logs-es").aliases("logs")); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().admin().indices().aliases(aliasesAddRequest).actionGet() + ); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (logs)")); + } + { + assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request("*")).actionGet()); + DataStreamIT.putComposableIndexTemplate( + "my-template", + null, + List.of("logs-*"), + null, + null, + Map.of("logs", AliasMetadata.builder("logs").build()) + ); + + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("logs-es"); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().execute(CreateDataStreamAction.INSTANCE, request).actionGet() + ); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (logs)")); + } + } + + public void testCreateDataStreamAliasWithSameNameAsIndex() throws Exception { + DataStreamIT.putComposableIndexTemplate("my-template", List.of("logs-*")); + + CreateIndexRequest createIndexRequest = new CreateIndexRequest("logs"); + assertAcked(client().admin().indices().create(createIndexRequest).actionGet()); + + { + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("logs-es"); + assertAcked(client().execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest(); + aliasesAddRequest.addAliasAction(new AliasActions(AliasActions.Type.ADD).index("logs-es").aliases("logs")); + Exception e = expectThrows( + InvalidAliasNameException.class, + () -> client().admin().indices().aliases(aliasesAddRequest).actionGet() + ); + assertThat( + e.getMessage(), + equalTo("Invalid alias name [logs]: an index or data stream exists with the same name as the alias") + ); + } + { + assertAcked(client().execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request("*")).actionGet()); + Exception e = expectThrows( + IllegalArgumentException.class, + () -> DataStreamIT.putComposableIndexTemplate( + "my-template", + null, + List.of("logs-*"), + null, + null, + Map.of("logs", AliasMetadata.builder("logs").build()) + ) + ); + assertThat( + e.getCause().getMessage(), + equalTo("Invalid alias name [logs]: an index or data stream exists with the same name as the alias") + ); + } + } + + public void testCreateIndexWithSameNameAsDataStreamAlias() throws Exception { + DataStreamIT.putComposableIndexTemplate("my-template", List.of("logs-*")); + + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("logs-es"); + assertAcked(client().execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest(); + aliasesAddRequest.addAliasAction(new AliasActions(AliasActions.Type.ADD).index("logs-es").aliases("logs")); + assertAcked(client().admin().indices().aliases(aliasesAddRequest).actionGet()); + + CreateIndexRequest createIndexRequest = new CreateIndexRequest("logs"); + Exception e = expectThrows( + InvalidIndexNameException.class, + () -> client().admin().indices().create(createIndexRequest).actionGet() + ); + assertThat(e.getMessage(), equalTo("Invalid index name [logs], already exists as alias")); + } + + public void testCreateIndexAliasWithSameNameAsDataStreamAlias() throws Exception { + DataStreamIT.putComposableIndexTemplate("my-template", List.of("logs-*")); + + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("logs-es"); + assertAcked(client().execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest(); + aliasesAddRequest.addAliasAction(new AliasActions(AliasActions.Type.ADD).index("logs-es").aliases("logs")); + assertAcked(client().admin().indices().aliases(aliasesAddRequest).actionGet()); + + { + CreateIndexRequest createIndexRequest = new CreateIndexRequest("my-index").alias(new Alias("logs")); + Exception e = expectThrows( + IllegalStateException.class, + () -> client().admin().indices().create(createIndexRequest).actionGet() + ); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (logs)")); + } + { + CreateIndexRequest createIndexRequest = new CreateIndexRequest("my-index"); + assertAcked(client().admin().indices().create(createIndexRequest).actionGet()); + IndicesAliasesRequest addAliasRequest = new IndicesAliasesRequest(); + addAliasRequest.addAliasAction(new AliasActions(AliasActions.Type.ADD).index("my-index").aliases("logs")); + Exception e = expectThrows(IllegalStateException.class, () -> client().admin().indices().aliases(addAliasRequest).actionGet()); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (logs)")); + } + } + private static void verifyResolvability(String dataStream, ActionRequestBuilder requestBuilder, boolean fail) { verifyResolvability(dataStream, requestBuilder, fail, 0); } @@ -1581,12 +1773,23 @@ static void putComposableIndexTemplate( java.util.List patterns, @Nullable Settings settings, @Nullable java.util.Map metadata + ) throws IOException { + putComposableIndexTemplate(id, mappings, patterns, settings, metadata, null); + } + + static void putComposableIndexTemplate( + String id, + @Nullable String mappings, + java.util.List patterns, + @Nullable Settings settings, + @Nullable java.util.Map metadata, + @Nullable java.util.Map aliases ) throws IOException { PutComposableIndexTemplateAction.Request request = new PutComposableIndexTemplateAction.Request(id); request.indexTemplate( new ComposableIndexTemplate( patterns, - new Template(settings, mappings == null ? null : new CompressedXContent(mappings), null), + new Template(settings, mappings == null ? null : new CompressedXContent(mappings), aliases), null, null, null, diff --git a/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java b/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java index 2d86d4741aa16..cb4ccee7cbe26 100644 --- a/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java +++ b/x-pack/plugin/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamsSnapshotsIT.java @@ -14,13 +14,16 @@ import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest; import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; +import org.elasticsearch.action.admin.indices.alias.Alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; import org.elasticsearch.action.admin.indices.close.CloseIndexRequest; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; +import org.elasticsearch.action.admin.indices.template.delete.DeleteComposableIndexTemplateAction; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.master.AcknowledgedResponse; @@ -1080,4 +1083,40 @@ public void testRestoreSnapshotFully() throws Exception { assertThat(client.execute(GetDataStreamAction.INSTANCE, getRequest).get().getDataStreams(), hasSize(2)); assertNotNull(client.admin().indices().prepareGetIndex().setIndices(indexName).get()); } + + public void testRestoreDataStreamAliasWithConflictingDataStream() throws Exception { + String snapshotName = "test-snapshot"; + createFullSnapshot(REPO, snapshotName); + client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request("*")).actionGet(); + DataStreamIT.putComposableIndexTemplate("my-template", List.of("my-*")); + try { + CreateDataStreamAction.Request request = new CreateDataStreamAction.Request("my-alias"); + assertAcked(client.execute(CreateDataStreamAction.INSTANCE, request).actionGet()); + Exception e = expectThrows( + IllegalStateException.class, + () -> client.admin().cluster().prepareRestoreSnapshot(REPO, snapshotName).setWaitForCompletion(true).get() + ); + assertThat(e.getMessage(), containsString("data stream alias and data stream have the same name (my-alias)")); + } finally { + // Need to remove data streams in order to remove template + client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request("*")).actionGet(); + // Need to remove template, because base class doesn't remove composable index templates after each test (only legacy templates) + client.execute(DeleteComposableIndexTemplateAction.INSTANCE, new DeleteComposableIndexTemplateAction.Request("my-template")) + .actionGet(); + } + } + + public void testRestoreDataStreamAliasWithConflictingIndicesAlias() throws Exception { + String snapshotName = "test-snapshot"; + createFullSnapshot(REPO, snapshotName); + client.execute(DeleteDataStreamAction.INSTANCE, new DeleteDataStreamAction.Request("*")).actionGet(); + CreateIndexRequest createIndexRequest = new CreateIndexRequest("my-index").alias(new Alias("my-alias")); + assertAcked(client.admin().indices().create(createIndexRequest).actionGet()); + + Exception e = expectThrows( + IllegalStateException.class, + () -> client.admin().cluster().prepareRestoreSnapshot(REPO, snapshotName).setWaitForCompletion(true).get() + ); + assertThat(e.getMessage(), containsString("data stream alias and indices alias have the same name (my-alias)")); + } }