Skip to content

Commit fdd4537

Browse files
authored
Fix NPE in rolling over unknown target and return 404 (#125352)
Since #122905 we were throwing NPEs (i.e. 5xxs) when a rollover request has an unknown/non-existent target. Before that, we returned a 400 - illegal argument exception. We now return a 404 which matches "missing target" better. Additionally, to avoid this from happening again, we add a YAML test that asserts the correct exception behavior.
1 parent 50437e7 commit fdd4537

File tree

6 files changed

+41
-24
lines changed

6 files changed

+41
-24
lines changed

docs/changelog/125352.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 125352
2+
summary: Fix NPE in rolling over unknown target and return 404
3+
area: Indices APIs
4+
type: bug
5+
issues: []

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.rollover/10_basic.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,19 @@
183183
min_age: "0s"
184184
min_docs: 1
185185
- match: { error.reason: "Validation Failed: 1: at least one max_* rollover condition must be set when using min_* conditions;" }
186+
187+
---
188+
"Rolling over an unknown target should return 404":
189+
- requires:
190+
capabilities:
191+
- method: POST
192+
path: /{index}/_rollover
193+
capabilities: ['return-404-on-missing-target']
194+
test_runner_features: [capabilities]
195+
reason: Rollover used to return a 400, then it briefly returned a 500 due to an NPE, now it properly returns a 404
196+
197+
- do:
198+
catch: missing
199+
indices.rollover:
200+
alias: "non_existent"
201+
- match: {error.reason: "rollover target [non_existent] does not exist"}

server/src/main/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverService.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.apache.logging.log4j.LogManager;
1313
import org.apache.logging.log4j.Logger;
14+
import org.elasticsearch.ResourceNotFoundException;
1415
import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest;
1516
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
1617
import org.elasticsearch.action.datastreams.autosharding.AutoShardingResult;
@@ -151,7 +152,7 @@ public RolloverResult rolloverClusterState(
151152
@Nullable AutoShardingResult autoShardingResult,
152153
boolean isFailureStoreRollover
153154
) throws Exception {
154-
validate(currentState.metadata(), rolloverTarget, newIndexName, createIndexRequest, isFailureStoreRollover);
155+
validate(currentState.metadata(), rolloverTarget, newIndexName, createIndexRequest);
155156
final IndexAbstraction indexAbstraction = currentState.metadata().getIndicesLookup().get(rolloverTarget);
156157
return switch (indexAbstraction.getType()) {
157158
case ALIAS -> rolloverAlias(
@@ -193,7 +194,7 @@ public static NameResolution resolveRolloverNames(
193194
CreateIndexRequest createIndexRequest,
194195
boolean isFailureStoreRollover
195196
) {
196-
validate(project, rolloverTarget, newIndexName, createIndexRequest, isFailureStoreRollover);
197+
validate(project, rolloverTarget, newIndexName, createIndexRequest);
197198
final IndexAbstraction indexAbstraction = project.getIndicesLookup().get(rolloverTarget);
198199
return switch (indexAbstraction.getType()) {
199200
case ALIAS -> resolveAliasRolloverNames(project, indexAbstraction, newIndexName);
@@ -655,16 +656,10 @@ static void checkNoDuplicatedAliasInIndexTemplate(
655656
}
656657
}
657658

658-
static void validate(
659-
ProjectMetadata project,
660-
String rolloverTarget,
661-
String newIndexName,
662-
CreateIndexRequest request,
663-
boolean isFailureStoreRollover
664-
) {
659+
static void validate(ProjectMetadata project, String rolloverTarget, String newIndexName, CreateIndexRequest request) {
665660
final IndexAbstraction indexAbstraction = project.getIndicesLookup().get(rolloverTarget);
666661
if (indexAbstraction == null) {
667-
throw new IllegalArgumentException("rollover target [" + rolloverTarget + "] does not exist");
662+
throw new ResourceNotFoundException("rollover target [" + rolloverTarget + "] does not exist");
668663
}
669664
if (VALID_ROLLOVER_TARGETS.contains(indexAbstraction.getType()) == false) {
670665
throw new IllegalArgumentException(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ protected ClusterBlockException checkBlock(RolloverRequest request, ClusterState
164164
ResolvedExpression resolvedRolloverTarget = SelectorResolver.parseExpression(request.getRolloverTarget(), request.indicesOptions());
165165
final IndexAbstraction indexAbstraction = projectMetadata.getIndicesLookup().get(resolvedRolloverTarget.resource());
166166
final String[] indicesToCheck;
167-
if (indexAbstraction.getType().equals(IndexAbstraction.Type.DATA_STREAM)) {
167+
if (indexAbstraction != null && indexAbstraction.getType().equals(IndexAbstraction.Type.DATA_STREAM)) {
168168
DataStream dataStream = (DataStream) indexAbstraction;
169169
boolean targetFailureStore = resolvedRolloverTarget.selector() != null
170170
&& resolvedRolloverTarget.selector().shouldIncludeFailures();

server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestRolloverIndexAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ public String getName() {
4444
@Override
4545
public Set<String> supportedCapabilities() {
4646
if (DataStream.isFailureStoreFeatureFlagEnabled()) {
47-
return Set.of("lazy-rollover-failure-store", "index-expression-selectors");
47+
return Set.of("return-404-on-missing-target", "lazy-rollover-failure-store", "index-expression-selectors");
4848
} else {
49-
return Set.of();
49+
return Set.of("return-404-on-missing-target");
5050
}
5151
}
5252

server/src/test/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverServiceTests.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.action.admin.indices.rollover;
1111

12+
import org.elasticsearch.ResourceNotFoundException;
1213
import org.elasticsearch.action.admin.indices.alias.Alias;
1314
import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest;
1415
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
@@ -203,23 +204,23 @@ public void testAliasValidation() {
203204
ProjectMetadata metadata = metadataBuilder.build();
204205
CreateIndexRequest req = new CreateIndexRequest();
205206

206-
IllegalArgumentException exception = expectThrows(
207+
Exception exception = expectThrows(
207208
IllegalArgumentException.class,
208-
() -> MetadataRolloverService.validate(metadata, aliasWithNoWriteIndex, randomAlphaOfLength(5), req, false)
209+
() -> MetadataRolloverService.validate(metadata, aliasWithNoWriteIndex, randomAlphaOfLength(5), req)
209210
);
210211
assertThat(exception.getMessage(), equalTo("rollover target [" + aliasWithNoWriteIndex + "] does not point to a write index"));
211212
exception = expectThrows(
212213
IllegalArgumentException.class,
213-
() -> MetadataRolloverService.validate(metadata, randomFrom(index1, index2), randomAlphaOfLength(5), req, false)
214+
() -> MetadataRolloverService.validate(metadata, randomFrom(index1, index2), randomAlphaOfLength(5), req)
214215
);
215216
assertThat(exception.getMessage(), equalTo("rollover target is a [concrete index] but one of [alias,data_stream] was expected"));
216217
final String aliasName = randomAlphaOfLength(5);
217218
exception = expectThrows(
218-
IllegalArgumentException.class,
219-
() -> MetadataRolloverService.validate(metadata, aliasName, randomAlphaOfLength(5), req, false)
219+
ResourceNotFoundException.class,
220+
() -> MetadataRolloverService.validate(metadata, aliasName, randomAlphaOfLength(5), req)
220221
);
221222
assertThat(exception.getMessage(), equalTo("rollover target [" + aliasName + "] does not exist"));
222-
MetadataRolloverService.validate(metadata, aliasWithWriteIndex, randomAlphaOfLength(5), req, false);
223+
MetadataRolloverService.validate(metadata, aliasWithWriteIndex, randomAlphaOfLength(5), req);
223224
}
224225

225226
public void testDataStreamValidation() throws IOException {
@@ -232,18 +233,18 @@ public void testDataStreamValidation() throws IOException {
232233
ProjectMetadata metadata = md.build();
233234
CreateIndexRequest req = new CreateIndexRequest();
234235

235-
MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, req, false);
236+
MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, req);
236237

237238
IllegalArgumentException exception = expectThrows(
238239
IllegalArgumentException.class,
239-
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), randomAlphaOfLength(5), req, false)
240+
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), randomAlphaOfLength(5), req)
240241
);
241242
assertThat(exception.getMessage(), equalTo("new index name may not be specified when rolling over a data stream"));
242243

243244
CreateIndexRequest aliasReq = new CreateIndexRequest().alias(new Alias("no_aliases_permitted"));
244245
exception = expectThrows(
245246
IllegalArgumentException.class,
246-
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, aliasReq, false)
247+
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, aliasReq)
247248
);
248249
assertThat(
249250
exception.getMessage(),
@@ -254,7 +255,7 @@ public void testDataStreamValidation() throws IOException {
254255
CreateIndexRequest mappingReq = new CreateIndexRequest().mapping(mapping);
255256
exception = expectThrows(
256257
IllegalArgumentException.class,
257-
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, mappingReq, false)
258+
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, mappingReq)
258259
);
259260
assertThat(
260261
exception.getMessage(),
@@ -264,7 +265,7 @@ public void testDataStreamValidation() throws IOException {
264265
CreateIndexRequest settingReq = new CreateIndexRequest().settings(Settings.builder().put("foo", "bar"));
265266
exception = expectThrows(
266267
IllegalArgumentException.class,
267-
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, settingReq, false)
268+
() -> MetadataRolloverService.validate(metadata, randomDataStream.getName(), null, settingReq)
268269
);
269270
assertThat(
270271
exception.getMessage(),

0 commit comments

Comments
 (0)