Skip to content

Commit 8164489

Browse files
authored
Validate alias operations don't target data streams (#58327)
This adds validation to make sure alias operations (add, remove, remove index) don't target data streams or the backing indices.
1 parent 547bcd3 commit 8164489

File tree

5 files changed

+109
-3
lines changed

5 files changed

+109
-3
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.ExceptionsHelper;
2222
import org.elasticsearch.action.ActionRequestBuilder;
2323
import org.elasticsearch.action.DocWriteRequest;
24+
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
2425
import org.elasticsearch.action.admin.indices.datastream.CreateDataStreamAction;
2526
import org.elasticsearch.action.admin.indices.datastream.DeleteDataStreamAction;
2627
import org.elasticsearch.action.admin.indices.datastream.GetDataStreamAction;
@@ -433,6 +434,36 @@ public void testCannotDeleteComposableTemplateUsedByDataStream() throws Exceptio
433434
assertTrue(maybeE.isPresent());
434435
}
435436

437+
public void testAliasActionsFailOnDataStreams() throws Exception {
438+
createIndexTemplate("id1", "metrics-foo*", "@timestamp1");
439+
String dataStreamName = "metrics-foo";
440+
CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName);
441+
client().admin().indices().createDataStream(createDataStreamRequest).get();
442+
443+
IndicesAliasesRequest.AliasActions addAction = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
444+
.index(dataStreamName).aliases("foo");
445+
IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest();
446+
aliasesAddRequest.addAliasAction(addAction);
447+
expectFailure(dataStreamName, () -> client().admin().indices().aliases(aliasesAddRequest).actionGet());
448+
}
449+
450+
public void testAliasActionsFailOnDataStreamBackingIndices() throws Exception {
451+
createIndexTemplate("id1", "metrics-foo*", "@timestamp1");
452+
String dataStreamName = "metrics-foo";
453+
CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName);
454+
client().admin().indices().createDataStream(createDataStreamRequest).get();
455+
456+
String backingIndex = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
457+
IndicesAliasesRequest.AliasActions addAction = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
458+
.index(backingIndex).aliases("first_gen");
459+
IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest();
460+
aliasesAddRequest.addAliasAction(addAction);
461+
Exception e = expectThrows(IllegalArgumentException.class, () -> client().admin().indices().aliases(aliasesAddRequest).actionGet());
462+
assertThat(e.getMessage(), equalTo("The provided expressions [" + backingIndex
463+
+ "] match a backing index belonging to data stream [" + dataStreamName + "]. Data streams and their backing indices don't " +
464+
"support aliases."));
465+
}
466+
436467
private static void verifyResolvability(String dataStream, ActionRequestBuilder requestBuilder, boolean fail) {
437468
verifyResolvability(dataStream, requestBuilder, fail, 0);
438469
}

server/src/main/java/org/elasticsearch/action/admin/indices/alias/TransportIndicesAliasesAction.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.elasticsearch.cluster.block.ClusterBlockLevel;
3535
import org.elasticsearch.cluster.metadata.AliasAction;
3636
import org.elasticsearch.cluster.metadata.AliasMetadata;
37+
import org.elasticsearch.cluster.metadata.IndexAbstraction;
3738
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
3839
import org.elasticsearch.cluster.metadata.Metadata;
3940
import org.elasticsearch.cluster.metadata.MetadataIndexAliasesService;
@@ -110,16 +111,26 @@ protected void masterOperation(Task task, final IndicesAliasesRequest request, f
110111
//Expand the indices names
111112
List<AliasActions> actions = request.aliasActions();
112113
List<AliasAction> finalActions = new ArrayList<>();
113-
114114
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
115115
Set<String> aliases = new HashSet<>();
116116
for (AliasActions action : actions) {
117-
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request.indicesOptions(), action.indices());
117+
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request.indicesOptions(), false,
118+
action.indices());
119+
for (Index concreteIndex : concreteIndices) {
120+
IndexAbstraction indexAbstraction = state.metadata().getIndicesLookup().get(concreteIndex.getName());
121+
assert indexAbstraction != null : "invalid cluster metadata. index [" + concreteIndex.getName() + "] was not found";
122+
if (indexAbstraction.getParentDataStream() != null) {
123+
throw new IllegalArgumentException("The provided expressions [" + String.join(",", action.indices())
124+
+ "] match a backing index belonging to data stream [" + indexAbstraction.getParentDataStream().getName()
125+
+ "]. Data streams and their backing indices don't support aliases.");
126+
}
127+
}
118128
final Optional<Exception> maybeException = requestValidators.validateRequest(request, state, concreteIndices);
119129
if (maybeException.isPresent()) {
120130
listener.onFailure(maybeException.get());
121131
return;
122132
}
133+
123134
Collections.addAll(aliases, action.getOriginalAliases());
124135
for (final Index index : concreteIndices) {
125136
switch (action.actionType()) {

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public ClusterState execute(ClusterState currentState) {
9090
});
9191
}
9292

93-
/**
93+
/**
9494
* Handles the cluster state transition to a version that reflects the provided {@link AliasAction}s.
9595
*/
9696
public ClusterState applyAliasActions(ClusterState currentState, Iterable<AliasAction> actions) {
@@ -108,6 +108,7 @@ public ClusterState applyAliasActions(ClusterState currentState, Iterable<AliasA
108108
if (index == null) {
109109
throw new IndexNotFoundException(action.getIndex());
110110
}
111+
validateAliasTargetIsNotDSBackingIndex(currentState, action);
111112
indicesToDelete.add(index.getIndex());
112113
changed = true;
113114
}
@@ -128,6 +129,7 @@ public ClusterState applyAliasActions(ClusterState currentState, Iterable<AliasA
128129
if (index == null) {
129130
throw new IndexNotFoundException(action.getIndex());
130131
}
132+
validateAliasTargetIsNotDSBackingIndex(currentState, action);
131133
NewAliasValidator newAliasValidator = (alias, indexRouting, filter, writeIndex) -> {
132134
/* It is important that we look up the index using the metadata builder we are modifying so we can remove an
133135
* index and replace it with an alias. */
@@ -187,4 +189,13 @@ public ClusterState applyAliasActions(ClusterState currentState, Iterable<AliasA
187189
}
188190
}
189191

192+
private void validateAliasTargetIsNotDSBackingIndex(ClusterState currentState, AliasAction action) {
193+
IndexAbstraction indexAbstraction = currentState.metadata().getIndicesLookup().get(action.getIndex());
194+
assert indexAbstraction != null : "invalid cluster metadata. index [" + action.getIndex() + "] was not found";
195+
if (indexAbstraction.getParentDataStream() != null) {
196+
throw new IllegalArgumentException("The provided index [ " + action.getIndex()
197+
+ "] is a backing index belonging to data stream [" + indexAbstraction.getParentDataStream().getName()
198+
+ "]. Data streams and their backing indices don't support alias operations.");
199+
}
200+
}
190201
}

server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,40 @@ public void testIndicesAliasesRequestIgnoresAliases() {
16881688
}
16891689
}
16901690

1691+
public void testIndicesAliasesRequestTargetDataStreams() {
1692+
final String dataStreamName = "my-data-stream";
1693+
IndexMetadata backingIndex = createBackingIndex(dataStreamName, 1).build();
1694+
1695+
Metadata.Builder mdBuilder = Metadata.builder()
1696+
.put(backingIndex, false)
1697+
.put(new DataStream(dataStreamName, "ts", List.of(backingIndex.getIndex()), 1));
1698+
ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
1699+
1700+
{
1701+
IndicesAliasesRequest.AliasActions aliasActions = IndicesAliasesRequest.AliasActions.add().index(dataStreamName);
1702+
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
1703+
() -> indexNameExpressionResolver.concreteIndexNames(state, aliasActions, false));
1704+
assertEquals("The provided expression [" + dataStreamName + "] matches a data stream, specify the corresponding " +
1705+
"concrete indices instead.", iae.getMessage());
1706+
}
1707+
1708+
{
1709+
IndicesAliasesRequest.AliasActions aliasActions = IndicesAliasesRequest.AliasActions.add().index("my-data-*").alias("my-data");
1710+
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
1711+
() -> indexNameExpressionResolver.concreteIndexNames(state, aliasActions, false));
1712+
assertEquals("The provided expression [my-data-*] matches a data stream, specify the corresponding concrete indices instead.",
1713+
iae.getMessage());
1714+
}
1715+
1716+
{
1717+
IndicesAliasesRequest.AliasActions aliasActions = IndicesAliasesRequest.AliasActions.add().index(dataStreamName)
1718+
.alias("my-data");
1719+
String[] indices = indexNameExpressionResolver.concreteIndexNames(state, aliasActions, true);
1720+
assertEquals(1, indices.length);
1721+
assertEquals(backingIndex.getIndex().getName(), indices[0]);
1722+
}
1723+
}
1724+
16911725
public void testInvalidIndex() {
16921726
Metadata.Builder mdBuilder = Metadata.builder().put(indexBuilder("test"));
16931727
ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();

server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexAliasesServiceTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import static org.hamcrest.Matchers.contains;
4141
import static org.hamcrest.Matchers.containsString;
4242
import static org.hamcrest.Matchers.equalTo;
43+
import static org.hamcrest.Matchers.is;
4344
import static org.hamcrest.Matchers.startsWith;
4445
import static org.mockito.Matchers.any;
4546
import static org.mockito.Matchers.anySetOf;
@@ -487,6 +488,24 @@ public void testSimultaneousHiddenPropertyValidation() {
487488
}
488489
}
489490

491+
public void testAliasesForDataStreamBackingIndicesNotSupported() {
492+
String dataStreamName = "foo-stream";
493+
String backingIndexName = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
494+
IndexMetadata indexMetadata = IndexMetadata.builder(backingIndexName)
495+
.settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(1).build();
496+
ClusterState state = ClusterState.builder(ClusterName.DEFAULT)
497+
.metadata(
498+
Metadata.builder()
499+
.put(indexMetadata, true)
500+
.put(new DataStream(dataStreamName, "@timestamp", singletonList(indexMetadata.getIndex()))))
501+
.build();
502+
503+
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> service.applyAliasActions(state,
504+
singletonList(new AliasAction.Add(backingIndexName, "test", null, null, null, null, null))));
505+
assertThat(exception.getMessage(), is("The provided index [ .ds-foo-stream-000001] is a backing index belonging to data stream " +
506+
"[foo-stream]. Data streams and their backing indices don't support alias operations."));
507+
}
508+
490509
private ClusterState applyHiddenAliasMix(ClusterState before, Boolean isHidden1, Boolean isHidden2) {
491510
return service.applyAliasActions(before, Arrays.asList(
492511
new AliasAction.Add("test", "alias", null, null, null, null, isHidden1),

0 commit comments

Comments
 (0)