Skip to content

Commit 442b5a6

Browse files
Address code review comments from PR 123618
1 parent b5b877e commit 442b5a6

File tree

11 files changed

+168
-67
lines changed

11 files changed

+168
-67
lines changed

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/snapshot.get/10_basic.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,64 @@ setup:
303303
snapshot.delete:
304304
repository: test_repo_get_1
305305
snapshot: test_snapshot_no_repo_name
306+
307+
---
308+
"Get snapshot using state parameter":
309+
310+
- do:
311+
indices.create:
312+
index: test_index
313+
body:
314+
settings:
315+
number_of_shards: 1
316+
number_of_replicas: 0
317+
318+
- do:
319+
snapshot.create:
320+
repository: test_repo_get_1
321+
snapshot: test_snapshot_with_state_param
322+
wait_for_completion: true
323+
324+
- do:
325+
snapshot.get:
326+
repository: test_repo_get_1
327+
snapshot: test_snapshot_with_state_param
328+
state: SUCCESS
329+
330+
- is_true: snapshots
331+
- match: { snapshots.0.snapshot: test_snapshot_with_state_param }
332+
- match: { snapshots.0.state: SUCCESS }
333+
334+
- do:
335+
snapshot.get:
336+
repository: test_repo_get_1
337+
snapshot: test_snapshot_with_state_param
338+
state: SUCCESS,PARTIAL
339+
340+
- is_true: snapshots
341+
- match: { snapshots.0.snapshot: test_snapshot_with_state_param }
342+
- match: { snapshots.0.state: SUCCESS }
343+
344+
- do:
345+
snapshot.get:
346+
repository: test_repo_get_1
347+
snapshot: test_snapshot_with_state_param
348+
state: FAILED
349+
350+
- is_true: snapshots
351+
- length: { snapshots: 0 }
352+
353+
- do:
354+
catch: bad_request
355+
snapshot.get:
356+
repository: test_repo_get_1
357+
snapshot: test_snapshot_with_state_param
358+
state: FOO
359+
360+
- match: { error.type: "illegal_argument_exception" }
361+
- match: { error.reason: "No enum constant org.elasticsearch.snapshots.SnapshotState.FOO" }
362+
363+
- do:
364+
snapshot.delete:
365+
repository: test_repo_get_1
366+
snapshot: test_snapshot_with_state_param

server/src/internalClusterTest/java/org/elasticsearch/snapshots/GetSnapshotsIT.java

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.nio.file.Files;
5454
import java.nio.file.Path;
5555
import java.util.ArrayList;
56+
import java.util.Arrays;
5657
import java.util.Collection;
5758
import java.util.Collections;
5859
import java.util.EnumSet;
@@ -61,6 +62,7 @@
6162
import java.util.Map;
6263
import java.util.Objects;
6364
import java.util.Set;
65+
import java.util.function.Function;
6466
import java.util.function.Predicate;
6567
import java.util.stream.Collectors;
6668

@@ -70,7 +72,6 @@
7072
import static org.hamcrest.Matchers.hasSize;
7173
import static org.hamcrest.Matchers.in;
7274
import static org.hamcrest.Matchers.is;
73-
import static org.hamcrest.core.StringContains.containsString;
7475

7576
public class GetSnapshotsIT extends AbstractSnapshotIntegTestCase {
7677

@@ -641,47 +642,55 @@ public void testFilterByState() throws Exception {
641642
createRepository(repoName, "mock", repoPath);
642643

643644
// Create a successful snapshot
644-
String successSnapshot = "snapshot-success";
645-
createFullSnapshot(repoName, successSnapshot);
645+
createFullSnapshot(repoName, "snapshot-success");
646+
647+
final Function<EnumSet<SnapshotState>, List<SnapshotInfo>> getSnapshotsForStates = (states) -> {
648+
return clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).setStates(states).get().getSnapshots();
649+
};
646650

647651
// Fetch snapshots with state=SUCCESS
648-
GetSnapshotsResponse responseSuccess = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName)
649-
.setState(EnumSet.of(SnapshotState.SUCCESS))
650-
.get();
651-
assertThat(responseSuccess.getSnapshots(), hasSize(1));
652-
assertThat(responseSuccess.getSnapshots().get(0).state(), is(SnapshotState.SUCCESS));
652+
var snapshots = getSnapshotsForStates.apply(EnumSet.of(SnapshotState.SUCCESS));
653+
assertThat(snapshots, hasSize(1));
654+
assertThat(snapshots.getFirst().state(), is(SnapshotState.SUCCESS));
653655

654656
// Create a snapshot in progress
655-
String inProgressSnapshot = "snapshot-in-progress";
656657
blockAllDataNodes(repoName);
657-
startFullSnapshot(repoName, inProgressSnapshot);
658+
startFullSnapshot(repoName, "snapshot-in-progress");
658659
awaitNumberOfSnapshotsInProgress(1);
659660

660661
// Fetch snapshots with state=IN_PROGRESS
661-
GetSnapshotsResponse responseInProgress = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName)
662-
.setState(EnumSet.of(SnapshotState.IN_PROGRESS))
663-
.get();
664-
assertThat(responseInProgress.getSnapshots(), hasSize(1));
665-
assertThat(responseInProgress.getSnapshots().get(0).state(), is(SnapshotState.IN_PROGRESS));
662+
snapshots = getSnapshotsForStates.apply(EnumSet.of(SnapshotState.IN_PROGRESS));
663+
assertThat(snapshots, hasSize(1));
664+
assertThat(snapshots.getFirst().state(), is(SnapshotState.IN_PROGRESS));
666665

667666
// Fetch snapshots with multiple states (SUCCESS, IN_PROGRESS)
668-
GetSnapshotsResponse responseMultipleStates = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName)
669-
.setState(EnumSet.of(SnapshotState.SUCCESS, SnapshotState.IN_PROGRESS))
670-
.get();
671-
assertThat(responseMultipleStates.getSnapshots(), hasSize(2));
672-
assertTrue(responseMultipleStates.getSnapshots().stream().map(SnapshotInfo::state).toList().contains(SnapshotState.SUCCESS));
673-
assertTrue(responseMultipleStates.getSnapshots().stream().map(SnapshotInfo::state).toList().contains(SnapshotState.IN_PROGRESS));
667+
snapshots = getSnapshotsForStates.apply(EnumSet.of(SnapshotState.SUCCESS, SnapshotState.IN_PROGRESS));
668+
assertThat(snapshots, hasSize(2));
669+
var states = snapshots.stream().map(SnapshotInfo::state).collect(Collectors.toSet());
670+
assertTrue(states.contains(SnapshotState.SUCCESS));
671+
assertTrue(states.contains(SnapshotState.IN_PROGRESS));
674672

675673
// Fetch all snapshots (without state)
676-
GetSnapshotsResponse responseAll = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).get();
677-
assertThat(responseAll.getSnapshots(), hasSize(2));
674+
snapshots = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).get().getSnapshots();
675+
assertThat(snapshots, hasSize(2));
678676

679677
// Fetch snapshots with an invalid state
680678
IllegalArgumentException e = expectThrows(
681679
IllegalArgumentException.class,
682-
() -> clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).setState(EnumSet.of(SnapshotState.of("FOO"))).get()
680+
() -> getSnapshotsForStates.apply(EnumSet.of(SnapshotState.valueOf("FOO")))
683681
);
684-
assertThat(e.getMessage(), containsString("Unknown state name [FOO]"));
682+
assertThat(e.getMessage(), is("No enum constant org.elasticsearch.snapshots.SnapshotState.FOO"));
683+
684+
// Allow the IN_PROGRESS snapshot to finish, then verify GET using SUCCESS has results and IN_PROGRESS does not.
685+
unblockAllDataNodes(repoName);
686+
awaitNumberOfSnapshotsInProgress(0);
687+
snapshots = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).get().getSnapshots();
688+
assertThat(snapshots, hasSize(2));
689+
states = snapshots.stream().map(SnapshotInfo::state).collect(Collectors.toSet());
690+
assertThat(states, hasSize(1));
691+
assertTrue(states.contains(SnapshotState.SUCCESS));
692+
snapshots = getSnapshotsForStates.apply(EnumSet.of(SnapshotState.IN_PROGRESS));
693+
assertThat(snapshots, hasSize(0));
685694
}
686695

687696
// Create a snapshot that is guaranteed to have a unique start time and duration for tests around ordering by either.
@@ -963,15 +972,15 @@ public void testAllFeatures() {
963972
// INDICES and by SHARDS. The actual sorting behaviour for these cases is tested elsewhere, here we're just checking that sorting
964973
// interacts correctly with the other parameters to the API.
965974

966-
final EnumSet<SnapshotState> state = EnumSet.of(randomFrom(SnapshotState.values()));
967-
// Note: The selected state may not match any existing snapshots.
975+
final EnumSet<SnapshotState> states = EnumSet.copyOf(randomNonEmptySubsetOf(Arrays.asList(SnapshotState.values())));
976+
// Note: The selected state(s) may not match any existing snapshots.
968977
// The actual filtering behaviour for such cases is tested in the dedicated test.
969-
// Here we're just checking that state interacts correctly with the other parameters to the API.
978+
// Here we're just checking that states interacts correctly with the other parameters to the API.
979+
snapshotInfoPredicate = snapshotInfoPredicate.and(si -> states.contains(si.state()));
970980

971981
// compute the ordered sequence of snapshots which match the repository/snapshot name filters and SLM policy filter
972982
final var selectedSnapshots = snapshotInfos.stream()
973983
.filter(snapshotInfoPredicate)
974-
.filter(s -> state.contains(s.state()))
975984
.sorted(sortKey.getSnapshotInfoComparator(order))
976985
.toList();
977986

@@ -981,7 +990,7 @@ public void testAllFeatures() {
981990
// apply sorting params
982991
.sort(sortKey)
983992
.order(order)
984-
.state(state);
993+
.states(states);
985994

986995
// sometimes use ?from_sort_value to skip some items; note that snapshots skipped in this way are subtracted from
987996
// GetSnapshotsResponse.totalCount whereas snapshots skipped by ?after and ?offset are not
@@ -1069,7 +1078,7 @@ public void testAllFeatures() {
10691078
.order(order)
10701079
.size(nextSize)
10711080
.after(SnapshotSortKey.decodeAfterQueryParam(nextRequestAfter))
1072-
.state(state);
1081+
.states(states);
10731082
final GetSnapshotsResponse nextResponse = safeAwait(l -> client().execute(TransportGetSnapshotsAction.TYPE, nextRequest, l));
10741083

10751084
assertEquals(

server/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@
424424
org.elasticsearch.action.bulk.BulkFeatures,
425425
org.elasticsearch.features.InfrastructureFeatures,
426426
org.elasticsearch.rest.action.admin.cluster.ClusterRerouteFeatures,
427+
org.elasticsearch.rest.action.admin.cluster.GetSnapshotsFeatures,
427428
org.elasticsearch.index.mapper.MapperFeatures,
428429
org.elasticsearch.index.IndexFeatures,
429430
org.elasticsearch.search.SearchFeatures,

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster, Predicate<
865865
registerHandler.accept(new RestDeleteRepositoryAction());
866866
registerHandler.accept(new RestVerifyRepositoryAction());
867867
registerHandler.accept(new RestCleanupRepositoryAction());
868-
registerHandler.accept(new RestGetSnapshotsAction());
868+
registerHandler.accept(new RestGetSnapshotsAction(clusterSupportsFeature));
869869
registerHandler.accept(new RestCreateSnapshotAction());
870870
registerHandler.accept(new RestCloneSnapshotAction());
871871
registerHandler.accept(new RestRestoreSnapshotAction());

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Arrays;
2929
import java.util.EnumSet;
3030
import java.util.Map;
31+
import java.util.Objects;
3132

3233
import static org.elasticsearch.action.ValidateActions.addValidationError;
3334

@@ -80,7 +81,7 @@ public class GetSnapshotsRequest extends MasterNodeRequest<GetSnapshotsRequest>
8081

8182
private boolean includeIndexNames = true;
8283

83-
private EnumSet<SnapshotState> state = EnumSet.noneOf(SnapshotState.class);
84+
private EnumSet<SnapshotState> states = EnumSet.allOf(SnapshotState.class);
8485

8586
public GetSnapshotsRequest(TimeValue masterNodeTimeout) {
8687
super(masterNodeTimeout);
@@ -124,7 +125,9 @@ public GetSnapshotsRequest(StreamInput in) throws IOException {
124125
includeIndexNames = in.readBoolean();
125126
}
126127
if (in.getTransportVersion().onOrAfter(STATE_FLAG_VERSION)) {
127-
state = in.readEnumSet(SnapshotState.class);
128+
states = in.readEnumSet(SnapshotState.class);
129+
} else {
130+
states = EnumSet.allOf(SnapshotState.class);
128131
}
129132
}
130133

@@ -146,7 +149,11 @@ public void writeTo(StreamOutput out) throws IOException {
146149
out.writeBoolean(includeIndexNames);
147150
}
148151
if (out.getTransportVersion().onOrAfter(STATE_FLAG_VERSION)) {
149-
out.writeEnumSet(state);
152+
out.writeEnumSet(states);
153+
} else if (states.equals(EnumSet.allOf(SnapshotState.class)) == false) {
154+
final var errorString = "GetSnapshotsRequest [states] field is not supported on all nodes in the cluster";
155+
assert false : errorString;
156+
throw new IllegalStateException(errorString);
150157
}
151158
}
152159

@@ -353,12 +360,12 @@ public boolean verbose() {
353360
return verbose;
354361
}
355362

356-
public EnumSet<SnapshotState> state() {
357-
return state;
363+
public EnumSet<SnapshotState> states() {
364+
return states;
358365
}
359366

360-
public GetSnapshotsRequest state(EnumSet<SnapshotState> state) {
361-
this.state = state;
367+
public GetSnapshotsRequest states(EnumSet<SnapshotState> states) {
368+
this.states = Objects.requireNonNull(states);
362369
return this;
363370
}
364371

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ public GetSnapshotsRequestBuilder setIncludeIndexNames(boolean indices) {
153153

154154
}
155155

156-
public GetSnapshotsRequestBuilder setState(EnumSet<SnapshotState> state) {
157-
request.state(state);
156+
public GetSnapshotsRequestBuilder setStates(EnumSet<SnapshotState> states) {
157+
request.states(states);
158158
return this;
159159
}
160160
}

server/src/main/java/org/elasticsearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ protected void masterOperation(
163163
SnapshotsInProgress.get(state),
164164
request.verbose(),
165165
request.includeIndexNames(),
166-
request.state()
166+
request.states()
167167
).runOperation(listener);
168168
}
169169

@@ -184,7 +184,7 @@ private class GetSnapshotsOperation {
184184
private final SnapshotNamePredicate snapshotNamePredicate;
185185
private final SnapshotPredicates fromSortValuePredicates;
186186
private final Predicate<String> slmPolicyPredicate;
187-
private final EnumSet<SnapshotState> state;
187+
private final EnumSet<SnapshotState> states;
188188

189189
// snapshot ordering/pagination
190190
private final SnapshotSortKey sortBy;
@@ -229,7 +229,7 @@ private class GetSnapshotsOperation {
229229
SnapshotsInProgress snapshotsInProgress,
230230
boolean verbose,
231231
boolean indices,
232-
EnumSet<SnapshotState> state
232+
EnumSet<SnapshotState> states
233233
) {
234234
this.cancellableTask = cancellableTask;
235235
this.repositories = repositories;
@@ -242,7 +242,7 @@ private class GetSnapshotsOperation {
242242
this.snapshotsInProgress = snapshotsInProgress;
243243
this.verbose = verbose;
244244
this.indices = indices;
245-
this.state = state;
245+
this.states = states;
246246

247247
this.snapshotNamePredicate = SnapshotNamePredicate.forSnapshots(ignoreUnavailable, snapshots);
248248
this.fromSortValuePredicates = SnapshotPredicates.forFromSortValue(fromSortValue, sortBy, order);
@@ -566,7 +566,10 @@ private boolean matchesPredicates(SnapshotId snapshotId, RepositoryData reposito
566566

567567
final var details = repositoryData.getSnapshotDetails(snapshotId);
568568

569-
if (!state.isEmpty() && !state.contains(details.getSnapshotState())) {
569+
if (states.isEmpty() == false
570+
&& details != null
571+
&& details.getSnapshotState() != null
572+
&& states.contains(details.getSnapshotState()) == false) {
570573
return false;
571574
}
572575

@@ -582,6 +585,10 @@ private boolean matchesPredicates(SnapshotInfo snapshotInfo) {
582585
return false;
583586
}
584587

588+
if (states.isEmpty() == false && snapshotInfo.state() != null && states.contains(snapshotInfo.state()) == false) {
589+
return false;
590+
}
591+
585592
if (slmPolicyPredicate == SlmPolicyPredicate.MATCH_ALL_POLICIES) {
586593
return true;
587594
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.rest.action.admin.cluster;
11+
12+
import org.elasticsearch.features.FeatureSpecification;
13+
import org.elasticsearch.features.NodeFeature;
14+
15+
import java.util.Set;
16+
17+
public class GetSnapshotsFeatures implements FeatureSpecification {
18+
public static final NodeFeature GET_SNAPSHOTS_STATE_PARAMETER = new NodeFeature("get.snapshots.state_parameter");
19+
20+
@Override
21+
public Set<NodeFeature> getFeatures() {
22+
return Set.of(GET_SNAPSHOTS_STATE_PARAMETER);
23+
}
24+
}

0 commit comments

Comments
 (0)