Skip to content

Commit 4963699

Browse files
authored
Fail rollover if duplicated alias found in template (#28110)
If a newly created index from a rollover request matches with an index template whose aliases contains the rollover request alias, the alias will point to multiple indices. This will cause indexing requests to be rejected. To avoid such situation, we make sure that there is no duplicated alias before creating a new index; otherwise abort and report an error to the caller. Closes #26976
1 parent 04ce0e7 commit 4963699

File tree

6 files changed

+82
-19
lines changed

6 files changed

+82
-19
lines changed

core/src/main/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverAction.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@
3737
import org.elasticsearch.cluster.metadata.AliasOrIndex;
3838
import org.elasticsearch.cluster.metadata.IndexMetaData;
3939
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
40+
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
4041
import org.elasticsearch.cluster.metadata.MetaData;
4142
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
4243
import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService;
44+
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
4345
import org.elasticsearch.cluster.service.ClusterService;
4446
import org.elasticsearch.common.inject.Inject;
4547
import org.elasticsearch.common.settings.Settings;
@@ -115,6 +117,7 @@ protected void masterOperation(final RolloverRequest rolloverRequest, final Clus
115117
: generateRolloverIndexName(sourceProvidedName, indexNameExpressionResolver);
116118
final String rolloverIndexName = indexNameExpressionResolver.resolveDateMathExpression(unresolvedName);
117119
MetaDataCreateIndexService.validateIndexName(rolloverIndexName, state); // will fail if the index already exists
120+
checkNoDuplicatedAliasInIndexTemplate(metaData, rolloverIndexName, rolloverRequest.getAlias());
118121
client.admin().indices().prepareStats(sourceIndexName).clear().setDocs(true).execute(
119122
new ActionListener<IndicesStatsResponse>() {
120123
@Override
@@ -238,4 +241,19 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest(final Stri
238241
.mappings(createIndexRequest.mappings());
239242
}
240243

244+
/**
245+
* If the newly created index matches with an index template whose aliases contains the rollover alias,
246+
* the rollover alias will point to multiple indices. This causes indexing requests to be rejected.
247+
* To avoid this, we make sure that there is no duplicated alias in index templates before creating a new index.
248+
*/
249+
static void checkNoDuplicatedAliasInIndexTemplate(MetaData metaData, String rolloverIndexName, String rolloverRequestAlias) {
250+
final List<IndexTemplateMetaData> matchedTemplates = MetaDataIndexTemplateService.findTemplates(metaData, rolloverIndexName);
251+
for (IndexTemplateMetaData template : matchedTemplates) {
252+
if (template.aliases().containsKey(rolloverRequestAlias)) {
253+
throw new IllegalArgumentException(String.format(Locale.ROOT,
254+
"Rollover alias [%s] can point to multiple indices, found duplicated alias [%s] in index template [%s]",
255+
rolloverRequestAlias, template.aliases().keys(), template.name()));
256+
}
257+
}
258+
}
241259
}

core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataCreateIndexService.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public ClusterState execute(ClusterState currentState) throws Exception {
277277

278278
// we only find a template when its an API call (a new index)
279279
// find templates, highest order are better matching
280-
List<IndexTemplateMetaData> templates = findTemplates(request, currentState);
280+
List<IndexTemplateMetaData> templates = MetaDataIndexTemplateService.findTemplates(currentState.metaData(), request.index());
281281

282282
Map<String, Custom> customs = new HashMap<>();
283283

@@ -564,22 +564,6 @@ public void onFailure(String source, Exception e) {
564564
}
565565
super.onFailure(source, e);
566566
}
567-
568-
private List<IndexTemplateMetaData> findTemplates(CreateIndexClusterStateUpdateRequest request, ClusterState state) throws IOException {
569-
List<IndexTemplateMetaData> templateMetadata = new ArrayList<>();
570-
for (ObjectCursor<IndexTemplateMetaData> cursor : state.metaData().templates().values()) {
571-
IndexTemplateMetaData metadata = cursor.value;
572-
for (String template: metadata.patterns()) {
573-
if (Regex.simpleMatch(template, request.index())) {
574-
templateMetadata.add(metadata);
575-
break;
576-
}
577-
}
578-
}
579-
580-
CollectionUtil.timSort(templateMetadata, Comparator.comparingInt(IndexTemplateMetaData::order).reversed());
581-
return templateMetadata;
582-
}
583567
}
584568

585569
private void validate(CreateIndexClusterStateUpdateRequest request, ClusterState state) {

core/src/main/java/org/elasticsearch/cluster/metadata/MetaDataIndexTemplateService.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.carrotsearch.hppc.cursors.ObjectCursor;
2222

23+
import org.apache.lucene.util.CollectionUtil;
2324
import org.elasticsearch.Version;
2425
import org.elasticsearch.action.admin.indices.alias.Alias;
2526
import org.elasticsearch.action.support.master.MasterNodeRequest;
@@ -48,6 +49,7 @@
4849

4950
import java.util.ArrayList;
5051
import java.util.Collections;
52+
import java.util.Comparator;
5153
import java.util.HashMap;
5254
import java.util.HashSet;
5355
import java.util.List;
@@ -193,6 +195,23 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS
193195
});
194196
}
195197

198+
/**
199+
* Finds index templates whose index pattern matched with the given index name.
200+
* The result is sorted by {@link IndexTemplateMetaData#order} descending.
201+
*/
202+
public static List<IndexTemplateMetaData> findTemplates(MetaData metaData, String indexName) {
203+
final List<IndexTemplateMetaData> matchedTemplates = new ArrayList<>();
204+
for (ObjectCursor<IndexTemplateMetaData> cursor : metaData.templates().values()) {
205+
final IndexTemplateMetaData template = cursor.value;
206+
final boolean matched = template.patterns().stream().anyMatch(pattern -> Regex.simpleMatch(pattern, indexName));
207+
if (matched) {
208+
matchedTemplates.add(template);
209+
}
210+
}
211+
CollectionUtil.timSort(matchedTemplates, Comparator.comparingInt(IndexTemplateMetaData::order).reversed());
212+
return matchedTemplates;
213+
}
214+
196215
private static void validateAndAddTemplate(final PutRequest request, IndexTemplateMetaData.Builder templateBuilder,
197216
IndicesService indicesService, NamedXContentRegistry xContentRegistry) throws Exception {
198217
Index createdIndex = null;

core/src/test/java/org/elasticsearch/action/admin/indices/rollover/RolloverIT.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,15 @@ public void testRolloverMaxSize() throws Exception {
277277
assertThat("No rollover with an empty index", response.isRolledOver(), equalTo(false));
278278
}
279279
}
280+
281+
public void testRejectIfAliasFoundInTemplate() throws Exception {
282+
client().admin().indices().preparePutTemplate("logs")
283+
.setPatterns(Collections.singletonList("logs-*")).addAlias(new Alias("logs-write")).get();
284+
assertAcked(client().admin().indices().prepareCreate("logs-000001").get());
285+
ensureYellow("logs-write");
286+
final IllegalArgumentException error = expectThrows(IllegalArgumentException.class,
287+
() -> client().admin().indices().prepareRolloverIndex("logs-write").addMaxIndexSizeCondition(new ByteSizeValue(1)).get());
288+
assertThat(error.getMessage(), equalTo(
289+
"Rollover alias [logs-write] can point to multiple indices, found duplicated alias [[logs-write]] in index template [logs]"));
290+
}
280291
}

core/src/test/java/org/elasticsearch/action/admin/indices/rollover/TransportRolloverActionTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.cluster.metadata.AliasMetaData;
3030
import org.elasticsearch.cluster.metadata.IndexMetaData;
3131
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
32+
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
3233
import org.elasticsearch.cluster.metadata.MetaData;
3334
import org.elasticsearch.common.UUIDs;
3435
import org.elasticsearch.common.settings.Settings;
@@ -40,11 +41,13 @@
4041
import org.elasticsearch.test.ESTestCase;
4142
import org.mockito.ArgumentCaptor;
4243

44+
import java.util.Arrays;
4345
import java.util.List;
4446
import java.util.Locale;
4547
import java.util.Set;
4648

4749
import static org.elasticsearch.action.admin.indices.rollover.TransportRolloverAction.evaluateConditions;
50+
import static org.hamcrest.Matchers.containsString;
4851
import static org.hamcrest.Matchers.equalTo;
4952
import static org.hamcrest.Matchers.hasSize;
5053
import static org.mockito.Matchers.any;
@@ -241,6 +244,19 @@ public void testCreateIndexRequest() throws Exception {
241244
assertThat(createIndexRequest.cause(), equalTo("rollover_index"));
242245
}
243246

247+
public void testRejectDuplicateAlias() throws Exception {
248+
final IndexTemplateMetaData template = IndexTemplateMetaData.builder("test-template")
249+
.patterns(Arrays.asList("foo-*", "bar-*"))
250+
.putAlias(AliasMetaData.builder("foo-write")).putAlias(AliasMetaData.builder("bar-write"))
251+
.build();
252+
final MetaData metaData = MetaData.builder().put(createMetaData(), false).put(template).build();
253+
String indexName = randomFrom("foo-123", "bar-xyz");
254+
String aliasName = randomFrom("foo-write", "bar-write");
255+
final IllegalArgumentException ex = expectThrows(IllegalArgumentException.class,
256+
() -> TransportRolloverAction.checkNoDuplicatedAliasInIndexTemplate(metaData, indexName, aliasName));
257+
assertThat(ex.getMessage(), containsString("index template [test-template]"));
258+
}
259+
244260
private IndicesStatsResponse createIndicesStatResponse(long totalDocs, long primaryDocs) {
245261
final CommonStats primaryStats = mock(CommonStats.class);
246262
when(primaryStats.getDocs()).thenReturn(new DocsStats(primaryDocs, 0, between(1, 10000)));

core/src/test/java/org/elasticsearch/action/admin/indices/template/put/MetaDataIndexTemplateServiceTests.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
package org.elasticsearch.action.admin.indices.template.put;
2121

2222
import org.elasticsearch.action.admin.indices.alias.Alias;
23+
import org.elasticsearch.cluster.ClusterState;
2324
import org.elasticsearch.cluster.metadata.AliasValidator;
2425
import org.elasticsearch.cluster.metadata.IndexMetaData;
26+
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
2527
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
2628
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
2729
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService.PutRequest;
@@ -38,16 +40,17 @@
3840
import java.util.ArrayList;
3941
import java.util.Arrays;
4042
import java.util.Collections;
41-
import java.util.HashMap;
4243
import java.util.HashSet;
4344
import java.util.List;
44-
import java.util.Map;
4545
import java.util.Set;
4646
import java.util.concurrent.CountDownLatch;
47+
import java.util.stream.Collectors;
4748

4849
import static org.hamcrest.CoreMatchers.containsString;
4950
import static org.hamcrest.CoreMatchers.equalTo;
5051
import static org.hamcrest.CoreMatchers.instanceOf;
52+
import static org.hamcrest.Matchers.contains;
53+
import static org.hamcrest.Matchers.empty;
5154

5255
public class MetaDataIndexTemplateServiceTests extends ESSingleNodeTestCase {
5356
public void testIndexTemplateInvalidNumberOfShards() {
@@ -154,6 +157,18 @@ public void testAliasInvalidFilterInvalidJson() throws Exception {
154157
assertThat(errors.get(0).getMessage(), equalTo("failed to parse filter for alias [invalid_alias]"));
155158
}
156159

160+
public void testFindTemplates() throws Exception {
161+
client().admin().indices().prepareDeleteTemplate("*").get(); // Delete all existing templates
162+
putTemplateDetail(new PutRequest("test", "foo-1").patterns(Arrays.asList("foo-*")).order(1));
163+
putTemplateDetail(new PutRequest("test", "foo-2").patterns(Arrays.asList("foo-*")).order(2));
164+
putTemplateDetail(new PutRequest("test", "bar").patterns(Arrays.asList("bar-*")).order(between(0, 100)));
165+
final ClusterState state = client().admin().cluster().prepareState().get().getState();
166+
assertThat(MetaDataIndexTemplateService.findTemplates(state.metaData(), "foo-1234").stream()
167+
.map(IndexTemplateMetaData::name).collect(Collectors.toList()), contains("foo-2", "foo-1"));
168+
assertThat(MetaDataIndexTemplateService.findTemplates(state.metaData(), "bar-xyz").stream()
169+
.map(IndexTemplateMetaData::name).collect(Collectors.toList()), contains("bar"));
170+
assertThat(MetaDataIndexTemplateService.findTemplates(state.metaData(), "baz"), empty());
171+
}
157172

158173
private static List<Throwable> putTemplate(NamedXContentRegistry xContentRegistry, PutRequest request) {
159174
MetaDataCreateIndexService createIndexService = new MetaDataCreateIndexService(

0 commit comments

Comments
 (0)