mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-25 14:26:27 +00:00
This adds validation to make sure alias operations (add, remove, remove index) don't target data streams or the backing indices. (cherry picked from commit 816448990e464a02f3960f12f6f6644a8cce36a4) Signed-off-by: Andrei Dan <andrei.dan@elastic.co>
This commit is contained in:
parent
4836236446
commit
30e777856f
@ -21,6 +21,7 @@ package org.elasticsearch.indices;
|
|||||||
import org.elasticsearch.ExceptionsHelper;
|
import org.elasticsearch.ExceptionsHelper;
|
||||||
import org.elasticsearch.action.ActionRequestBuilder;
|
import org.elasticsearch.action.ActionRequestBuilder;
|
||||||
import org.elasticsearch.action.DocWriteRequest;
|
import org.elasticsearch.action.DocWriteRequest;
|
||||||
|
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.datastream.CreateDataStreamAction;
|
import org.elasticsearch.action.admin.indices.datastream.CreateDataStreamAction;
|
||||||
import org.elasticsearch.action.admin.indices.datastream.DeleteDataStreamAction;
|
import org.elasticsearch.action.admin.indices.datastream.DeleteDataStreamAction;
|
||||||
import org.elasticsearch.action.admin.indices.datastream.GetDataStreamAction;
|
import org.elasticsearch.action.admin.indices.datastream.GetDataStreamAction;
|
||||||
@ -434,6 +435,36 @@ public class DataStreamIT extends ESIntegTestCase {
|
|||||||
assertTrue(maybeE.isPresent());
|
assertTrue(maybeE.isPresent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAliasActionsFailOnDataStreams() throws Exception {
|
||||||
|
createIndexTemplate("id1", "metrics-foo*", "@timestamp1");
|
||||||
|
String dataStreamName = "metrics-foo";
|
||||||
|
CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName);
|
||||||
|
client().admin().indices().createDataStream(createDataStreamRequest).get();
|
||||||
|
|
||||||
|
IndicesAliasesRequest.AliasActions addAction = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
|
||||||
|
.index(dataStreamName).aliases("foo");
|
||||||
|
IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest();
|
||||||
|
aliasesAddRequest.addAliasAction(addAction);
|
||||||
|
expectFailure(dataStreamName, () -> client().admin().indices().aliases(aliasesAddRequest).actionGet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAliasActionsFailOnDataStreamBackingIndices() throws Exception {
|
||||||
|
createIndexTemplate("id1", "metrics-foo*", "@timestamp1");
|
||||||
|
String dataStreamName = "metrics-foo";
|
||||||
|
CreateDataStreamAction.Request createDataStreamRequest = new CreateDataStreamAction.Request(dataStreamName);
|
||||||
|
client().admin().indices().createDataStream(createDataStreamRequest).get();
|
||||||
|
|
||||||
|
String backingIndex = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
|
||||||
|
IndicesAliasesRequest.AliasActions addAction = new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
|
||||||
|
.index(backingIndex).aliases("first_gen");
|
||||||
|
IndicesAliasesRequest aliasesAddRequest = new IndicesAliasesRequest();
|
||||||
|
aliasesAddRequest.addAliasAction(addAction);
|
||||||
|
Exception e = expectThrows(IllegalArgumentException.class, () -> client().admin().indices().aliases(aliasesAddRequest).actionGet());
|
||||||
|
assertThat(e.getMessage(), equalTo("The provided expressions [" + backingIndex
|
||||||
|
+ "] match a backing index belonging to data stream [" + dataStreamName + "]. Data streams and their backing indices don't " +
|
||||||
|
"support aliases."));
|
||||||
|
}
|
||||||
|
|
||||||
private static void verifyResolvability(String dataStream, ActionRequestBuilder requestBuilder, boolean fail) {
|
private static void verifyResolvability(String dataStream, ActionRequestBuilder requestBuilder, boolean fail) {
|
||||||
verifyResolvability(dataStream, requestBuilder, fail, 0);
|
verifyResolvability(dataStream, requestBuilder, fail, 0);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import org.elasticsearch.cluster.block.ClusterBlockException;
|
|||||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||||
import org.elasticsearch.cluster.metadata.AliasAction;
|
import org.elasticsearch.cluster.metadata.AliasAction;
|
||||||
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexAbstraction;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.cluster.metadata.Metadata;
|
import org.elasticsearch.cluster.metadata.Metadata;
|
||||||
import org.elasticsearch.cluster.metadata.MetadataIndexAliasesService;
|
import org.elasticsearch.cluster.metadata.MetadataIndexAliasesService;
|
||||||
@ -109,16 +110,26 @@ public class TransportIndicesAliasesAction extends TransportMasterNodeAction<Ind
|
|||||||
//Expand the indices names
|
//Expand the indices names
|
||||||
List<AliasActions> actions = request.aliasActions();
|
List<AliasActions> actions = request.aliasActions();
|
||||||
List<AliasAction> finalActions = new ArrayList<>();
|
List<AliasAction> finalActions = new ArrayList<>();
|
||||||
|
|
||||||
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
// Resolve all the AliasActions into AliasAction instances and gather all the aliases
|
||||||
Set<String> aliases = new HashSet<>();
|
Set<String> aliases = new HashSet<>();
|
||||||
for (AliasActions action : actions) {
|
for (AliasActions action : actions) {
|
||||||
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request.indicesOptions(), action.indices());
|
final Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request.indicesOptions(), false,
|
||||||
|
action.indices());
|
||||||
|
for (Index concreteIndex : concreteIndices) {
|
||||||
|
IndexAbstraction indexAbstraction = state.metadata().getIndicesLookup().get(concreteIndex.getName());
|
||||||
|
assert indexAbstraction != null : "invalid cluster metadata. index [" + concreteIndex.getName() + "] was not found";
|
||||||
|
if (indexAbstraction.getParentDataStream() != null) {
|
||||||
|
throw new IllegalArgumentException("The provided expressions [" + String.join(",", action.indices())
|
||||||
|
+ "] match a backing index belonging to data stream [" + indexAbstraction.getParentDataStream().getName()
|
||||||
|
+ "]. Data streams and their backing indices don't support aliases.");
|
||||||
|
}
|
||||||
|
}
|
||||||
final Optional<Exception> maybeException = requestValidators.validateRequest(request, state, concreteIndices);
|
final Optional<Exception> maybeException = requestValidators.validateRequest(request, state, concreteIndices);
|
||||||
if (maybeException.isPresent()) {
|
if (maybeException.isPresent()) {
|
||||||
listener.onFailure(maybeException.get());
|
listener.onFailure(maybeException.get());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.addAll(aliases, action.getOriginalAliases());
|
Collections.addAll(aliases, action.getOriginalAliases());
|
||||||
for (final Index index : concreteIndices) {
|
for (final Index index : concreteIndices) {
|
||||||
switch (action.actionType()) {
|
switch (action.actionType()) {
|
||||||
|
@ -90,7 +90,7 @@ public class MetadataIndexAliasesService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the cluster state transition to a version that reflects the provided {@link AliasAction}s.
|
* Handles the cluster state transition to a version that reflects the provided {@link AliasAction}s.
|
||||||
*/
|
*/
|
||||||
public ClusterState applyAliasActions(ClusterState currentState, Iterable<AliasAction> actions) {
|
public ClusterState applyAliasActions(ClusterState currentState, Iterable<AliasAction> actions) {
|
||||||
@ -108,6 +108,7 @@ public class MetadataIndexAliasesService {
|
|||||||
if (index == null) {
|
if (index == null) {
|
||||||
throw new IndexNotFoundException(action.getIndex());
|
throw new IndexNotFoundException(action.getIndex());
|
||||||
}
|
}
|
||||||
|
validateAliasTargetIsNotDSBackingIndex(currentState, action);
|
||||||
indicesToDelete.add(index.getIndex());
|
indicesToDelete.add(index.getIndex());
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@ -128,6 +129,7 @@ public class MetadataIndexAliasesService {
|
|||||||
if (index == null) {
|
if (index == null) {
|
||||||
throw new IndexNotFoundException(action.getIndex());
|
throw new IndexNotFoundException(action.getIndex());
|
||||||
}
|
}
|
||||||
|
validateAliasTargetIsNotDSBackingIndex(currentState, action);
|
||||||
NewAliasValidator newAliasValidator = (alias, indexRouting, filter, writeIndex) -> {
|
NewAliasValidator newAliasValidator = (alias, indexRouting, filter, writeIndex) -> {
|
||||||
/* It is important that we look up the index using the metadata builder we are modifying so we can remove an
|
/* It is important that we look up the index using the metadata builder we are modifying so we can remove an
|
||||||
* index and replace it with an alias. */
|
* index and replace it with an alias. */
|
||||||
@ -187,4 +189,13 @@ public class MetadataIndexAliasesService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateAliasTargetIsNotDSBackingIndex(ClusterState currentState, AliasAction action) {
|
||||||
|
IndexAbstraction indexAbstraction = currentState.metadata().getIndicesLookup().get(action.getIndex());
|
||||||
|
assert indexAbstraction != null : "invalid cluster metadata. index [" + action.getIndex() + "] was not found";
|
||||||
|
if (indexAbstraction.getParentDataStream() != null) {
|
||||||
|
throw new IllegalArgumentException("The provided index [ " + action.getIndex()
|
||||||
|
+ "] is a backing index belonging to data stream [" + indexAbstraction.getParentDataStream().getName()
|
||||||
|
+ "]. Data streams and their backing indices don't support alias operations.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1688,6 +1688,40 @@ public class IndexNameExpressionResolverTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testIndicesAliasesRequestTargetDataStreams() {
|
||||||
|
final String dataStreamName = "my-data-stream";
|
||||||
|
IndexMetadata backingIndex = createBackingIndex(dataStreamName, 1).build();
|
||||||
|
|
||||||
|
Metadata.Builder mdBuilder = Metadata.builder()
|
||||||
|
.put(backingIndex, false)
|
||||||
|
.put(new DataStream(dataStreamName, "ts", org.elasticsearch.common.collect.List.of(backingIndex.getIndex()), 1));
|
||||||
|
ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
|
||||||
|
|
||||||
|
{
|
||||||
|
IndicesAliasesRequest.AliasActions aliasActions = IndicesAliasesRequest.AliasActions.add().index(dataStreamName);
|
||||||
|
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> indexNameExpressionResolver.concreteIndexNames(state, aliasActions, false));
|
||||||
|
assertEquals("The provided expression [" + dataStreamName + "] matches a data stream, specify the corresponding " +
|
||||||
|
"concrete indices instead.", iae.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
IndicesAliasesRequest.AliasActions aliasActions = IndicesAliasesRequest.AliasActions.add().index("my-data-*").alias("my-data");
|
||||||
|
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> indexNameExpressionResolver.concreteIndexNames(state, aliasActions, false));
|
||||||
|
assertEquals("The provided expression [my-data-*] matches a data stream, specify the corresponding concrete indices instead.",
|
||||||
|
iae.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
IndicesAliasesRequest.AliasActions aliasActions = IndicesAliasesRequest.AliasActions.add().index(dataStreamName)
|
||||||
|
.alias("my-data");
|
||||||
|
String[] indices = indexNameExpressionResolver.concreteIndexNames(state, aliasActions, true);
|
||||||
|
assertEquals(1, indices.length);
|
||||||
|
assertEquals(backingIndex.getIndex().getName(), indices[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testInvalidIndex() {
|
public void testInvalidIndex() {
|
||||||
Metadata.Builder mdBuilder = Metadata.builder().put(indexBuilder("test"));
|
Metadata.Builder mdBuilder = Metadata.builder().put(indexBuilder("test"));
|
||||||
ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
|
ClusterState state = ClusterState.builder(new ClusterName("_name")).metadata(mdBuilder).build();
|
||||||
|
@ -41,6 +41,7 @@ import static java.util.Collections.singletonList;
|
|||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anySetOf;
|
import static org.mockito.Matchers.anySetOf;
|
||||||
@ -488,6 +489,24 @@ public class MetadataIndexAliasesServiceTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAliasesForDataStreamBackingIndicesNotSupported() {
|
||||||
|
String dataStreamName = "foo-stream";
|
||||||
|
String backingIndexName = DataStream.getDefaultBackingIndexName(dataStreamName, 1);
|
||||||
|
IndexMetadata indexMetadata = IndexMetadata.builder(backingIndexName)
|
||||||
|
.settings(settings(Version.CURRENT)).numberOfShards(1).numberOfReplicas(1).build();
|
||||||
|
ClusterState state = ClusterState.builder(ClusterName.DEFAULT)
|
||||||
|
.metadata(
|
||||||
|
Metadata.builder()
|
||||||
|
.put(indexMetadata, true)
|
||||||
|
.put(new DataStream(dataStreamName, "@timestamp", singletonList(indexMetadata.getIndex()))))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> service.applyAliasActions(state,
|
||||||
|
singletonList(new AliasAction.Add(backingIndexName, "test", null, null, null, null, null))));
|
||||||
|
assertThat(exception.getMessage(), is("The provided index [ .ds-foo-stream-000001] is a backing index belonging to data stream " +
|
||||||
|
"[foo-stream]. Data streams and their backing indices don't support alias operations."));
|
||||||
|
}
|
||||||
|
|
||||||
private ClusterState applyHiddenAliasMix(ClusterState before, Boolean isHidden1, Boolean isHidden2) {
|
private ClusterState applyHiddenAliasMix(ClusterState before, Boolean isHidden1, Boolean isHidden2) {
|
||||||
return service.applyAliasActions(before, Arrays.asList(
|
return service.applyAliasActions(before, Arrays.asList(
|
||||||
new AliasAction.Add("test", "alias", null, null, null, null, isHidden1),
|
new AliasAction.Add("test", "alias", null, null, null, null, isHidden1),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user