Skip to content

Commit 5024ae0

Browse files
authored
Disallow deletion of composable template if in use by data stream (#57957)
Currently a data stream requires a component template in order to be created. This is so that the stream can be perpetuated when the index is automatically or manually rolled over. This commit ensures that the composable index template used by the stream is not removed if the data stream still exists. Resolves #57004
1 parent d7609c2 commit 5024ae0

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

server/src/internalClusterTest/java/org/elasticsearch/action/bulk/BulkIntegrationIT.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.Version;
2525
import org.elasticsearch.action.ActionRequestValidationException;
2626
import org.elasticsearch.action.admin.indices.alias.Alias;
27+
import org.elasticsearch.action.admin.indices.datastream.DeleteDataStreamAction;
2728
import org.elasticsearch.action.admin.indices.datastream.GetDataStreamAction;
2829
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
2930
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
@@ -270,6 +271,8 @@ public void testMixedAutoCreate() throws Exception {
270271
assertThat(getIndexResponse.getIndices(), hasItemInArray("logs-barfoo2"));
271272
assertThat(getIndexResponse.getIndices(), hasItemInArray("logs-barfoo3"));
272273

274+
DeleteDataStreamAction.Request deleteDSReq = new DeleteDataStreamAction.Request("*");
275+
client().execute(DeleteDataStreamAction.INSTANCE, deleteDSReq).actionGet();
273276
DeleteComposableIndexTemplateAction.Request deleteTemplateRequest = new DeleteComposableIndexTemplateAction.Request("*");
274277
client().execute(DeleteComposableIndexTemplateAction.INSTANCE, deleteTemplateRequest).actionGet();
275278
}

server/src/internalClusterTest/java/org/elasticsearch/indices/DataStreamIT.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.elasticsearch.indices;
2020

21+
import org.elasticsearch.ExceptionsHelper;
2122
import org.elasticsearch.action.ActionRequestBuilder;
2223
import org.elasticsearch.action.DocWriteRequest;
2324
import org.elasticsearch.action.admin.indices.datastream.CreateDataStreamAction;
@@ -60,6 +61,7 @@
6061
import java.util.Arrays;
6162
import java.util.Comparator;
6263
import java.util.List;
64+
import java.util.Optional;
6365

6466
import static org.elasticsearch.indices.IndicesOptionsIntegrationIT._flush;
6567
import static org.elasticsearch.indices.IndicesOptionsIntegrationIT.clearCache;
@@ -86,6 +88,8 @@ public class DataStreamIT extends ESIntegTestCase {
8688

8789
@After
8890
public void deleteAllComposableTemplates() {
91+
DeleteDataStreamAction.Request deleteDSRequest = new DeleteDataStreamAction.Request("*");
92+
client().execute(DeleteDataStreamAction.INSTANCE, deleteDSRequest).actionGet();
8993
DeleteComposableIndexTemplateAction.Request deleteTemplateRequest = new DeleteComposableIndexTemplateAction.Request("*");
9094
client().execute(DeleteComposableIndexTemplateAction.INSTANCE, deleteTemplateRequest).actionGet();
9195
}
@@ -362,6 +366,29 @@ public void testResolvabilityOfDataStreamsInAPIs() throws Exception {
362366
verifyResolvability(wildcardExpression, client().admin().indices().prepareGetIndex().addIndices(wildcardExpression), false);
363367
}
364368

369+
public void testCannotDeleteComposableTemplateUsedByDataStream() throws Exception {
370+
createIndexTemplate("id", "metrics-foobar*", "@timestamp1");
371+
String dataStreamName = "metrics-foobar-baz";
372+
CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName);
373+
client().admin().indices().createDataStream(createDataStreamRequest).get();
374+
createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName + "-eggplant");
375+
client().admin().indices().createDataStream(createDataStreamRequest).get();
376+
377+
DeleteComposableIndexTemplateAction.Request req = new DeleteComposableIndexTemplateAction.Request("id");
378+
Exception e = expectThrows(Exception.class, () -> client().execute(DeleteComposableIndexTemplateAction.INSTANCE, req).get());
379+
Optional<Exception> maybeE = ExceptionsHelper.unwrapCausesAndSuppressed(e, err ->
380+
err.getMessage().contains("unable to remove composable templates [id] " +
381+
"as they are in use by a data streams [metrics-foobar-baz, metrics-foobar-baz-eggplant]"));
382+
assertTrue(maybeE.isPresent());
383+
384+
DeleteComposableIndexTemplateAction.Request req2 = new DeleteComposableIndexTemplateAction.Request("i*");
385+
Exception e2 = expectThrows(Exception.class, () -> client().execute(DeleteComposableIndexTemplateAction.INSTANCE, req2).get());
386+
maybeE = ExceptionsHelper.unwrapCausesAndSuppressed(e2, err ->
387+
err.getMessage().contains("unable to remove composable templates [id] " +
388+
"as they are in use by a data streams [metrics-foobar-baz, metrics-foobar-baz-eggplant]"));
389+
assertTrue(maybeE.isPresent());
390+
}
391+
365392
private static void verifyResolvability(String dataStream, ActionRequestBuilder requestBuilder, boolean fail) {
366393
verifyResolvability(dataStream, requestBuilder, fail, 0);
367394
}

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateService.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import java.util.Objects;
6969
import java.util.Optional;
7070
import java.util.Set;
71+
import java.util.TreeSet;
7172
import java.util.function.Predicate;
7273
import java.util.stream.Collectors;
7374

@@ -609,6 +610,17 @@ static ClusterState innerRemoveIndexTemplateV2(ClusterState currentState, String
609610
}
610611
throw new IndexTemplateMissingException(name);
611612
}
613+
614+
Optional<Set<String>> dataStreamsUsingTemplates = templateNames.stream()
615+
.map(templateName -> dataStreamsUsingTemplate(currentState, templateName))
616+
.reduce(Sets::union);
617+
dataStreamsUsingTemplates.ifPresent(set -> {
618+
if (set.size() > 0) {
619+
throw new IllegalArgumentException("unable to remove composable templates " + new TreeSet<>(templateNames) +
620+
" as they are in use by a data streams " + new TreeSet<>(set));
621+
}
622+
});
623+
612624
Metadata.Builder metadata = Metadata.builder(currentState.metadata());
613625
for (String templateName : templateNames) {
614626
logger.info("removing index template [{}]", templateName);
@@ -617,6 +629,18 @@ static ClusterState innerRemoveIndexTemplateV2(ClusterState currentState, String
617629
return ClusterState.builder(currentState).metadata(metadata).build();
618630
}
619631

632+
static Set<String> dataStreamsUsingTemplate(final ClusterState state, final String templateName) {
633+
final ComposableIndexTemplate template = state.metadata().templatesV2().get(templateName);
634+
if (template == null) {
635+
return Collections.emptySet();
636+
}
637+
final Set<String> dataStreams = state.metadata().dataStreams().keySet();
638+
Set<String> matches = new HashSet<>();
639+
template.indexPatterns().forEach(indexPattern ->
640+
matches.addAll(dataStreams.stream().filter(stream -> Regex.simpleMatch(indexPattern, stream)).collect(Collectors.toList())));
641+
return matches;
642+
}
643+
620644
public void putTemplate(final PutRequest request, final PutListener listener) {
621645
Settings.Builder updatedSettingsBuilder = Settings.builder();
622646
updatedSettingsBuilder.put(request.settings).normalizePrefix(IndexMetadata.INDEX_SETTING_PREFIX);

0 commit comments

Comments
 (0)