-
Notifications
You must be signed in to change notification settings - Fork 25.5k
Add state query param to Get snapshots API #123618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
18fd911
d80ec19
712138f
a5bc52e
91ae28b
d9f8faf
f6f0b54
c2b19d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,7 @@ | |
import static org.hamcrest.Matchers.hasSize; | ||
import static org.hamcrest.Matchers.in; | ||
import static org.hamcrest.Matchers.is; | ||
import static org.hamcrest.core.StringContains.containsString; | ||
|
||
public class GetSnapshotsIT extends AbstractSnapshotIntegTestCase { | ||
|
||
|
@@ -633,6 +634,57 @@ public void testRetrievingSnapshotsWhenRepositoryIsMissing() throws Exception { | |
expectThrows(RepositoryMissingException.class, multiRepoFuture::actionGet); | ||
} | ||
|
||
public void testFilterByState() throws Exception { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we also add this feature to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I added some checks there as well. Let me know if I need to add any other tests. |
||
final String repoName = "test-repo"; | ||
final Path repoPath = randomRepoPath(); | ||
createRepository(repoName, "mock", repoPath); | ||
maybeInitWithOldSnapshotVersion(repoName, repoPath); | ||
|
||
// Create a successful snapshot | ||
String successSnapshot = "snapshot-success"; | ||
createFullSnapshot(repoName, successSnapshot); | ||
|
||
// Create a snapshot in progress | ||
String inProgressSnapshot = "snapshot-in-progress"; | ||
blockAllDataNodes(repoName); | ||
startFullSnapshot(repoName, inProgressSnapshot); | ||
awaitNumberOfSnapshotsInProgress(1); | ||
awaitClusterState( | ||
state -> SnapshotsInProgress.get(state) | ||
.asStream() | ||
.flatMap(s -> s.shards().entrySet().stream()) | ||
.allMatch( | ||
e -> e.getKey().getIndexName().equals("test-index-1") == false | ||
|| e.getValue().state() == SnapshotsInProgress.ShardState.SUCCESS | ||
) | ||
); | ||
|
||
// Fetch all snapshots | ||
GetSnapshotsResponse responseAll = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).get(); | ||
assertThat(responseAll.getSnapshots(), hasSize(2)); | ||
|
||
// Fetch snapshots with state=SUCCESS | ||
GetSnapshotsResponse responseSuccess = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName) | ||
.setState(String.valueOf(SnapshotState.SUCCESS)) | ||
.get(); | ||
assertThat(responseSuccess.getSnapshots(), hasSize(1)); | ||
assertThat(responseSuccess.getSnapshots().get(0).state(), is(SnapshotState.SUCCESS)); | ||
|
||
// Fetch snapshots with state=IN_PROGRESS | ||
GetSnapshotsResponse responseInProgress = clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName) | ||
.setState(String.valueOf(SnapshotState.IN_PROGRESS)) | ||
.get(); | ||
assertThat(responseInProgress.getSnapshots(), hasSize(1)); | ||
assertThat(responseInProgress.getSnapshots().get(0).state(), is(SnapshotState.IN_PROGRESS)); | ||
|
||
// Fetch snapshots with invalid state | ||
IllegalArgumentException e = expectThrows( | ||
IllegalArgumentException.class, | ||
() -> clusterAdmin().prepareGetSnapshots(TEST_REQUEST_TIMEOUT, repoName).setState("INVALID_STATE").get() | ||
); | ||
assertThat(e.getMessage(), containsString("state must be SUCCESS, IN_PROGRESS, FAILED, PARTIAL, or INCOMPATIBLE")); | ||
} | ||
|
||
// Create a snapshot that is guaranteed to have a unique start time and duration for tests around ordering by either. | ||
// Don't use this with more than 3 snapshots on platforms with low-resolution clocks as the durations could always collide there | ||
// causing an infinite loop | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -19,6 +19,7 @@ | |||||
import org.elasticsearch.core.Nullable; | ||||||
import org.elasticsearch.core.TimeValue; | ||||||
import org.elasticsearch.search.sort.SortOrder; | ||||||
import org.elasticsearch.snapshots.SnapshotState; | ||||||
import org.elasticsearch.tasks.CancellableTask; | ||||||
import org.elasticsearch.tasks.Task; | ||||||
import org.elasticsearch.tasks.TaskId; | ||||||
|
@@ -39,6 +40,7 @@ public class GetSnapshotsRequest extends MasterNodeRequest<GetSnapshotsRequest> | |||||
public static final boolean DEFAULT_VERBOSE_MODE = true; | ||||||
|
||||||
private static final TransportVersion INDICES_FLAG_VERSION = TransportVersions.V_8_3_0; | ||||||
private static final TransportVersion STATE_FLAG_VERSION = TransportVersions.STATE_PARAM_GET_SNAPSHOT; | ||||||
|
||||||
public static final int NO_LIMIT = -1; | ||||||
|
||||||
|
@@ -77,6 +79,8 @@ public class GetSnapshotsRequest extends MasterNodeRequest<GetSnapshotsRequest> | |||||
|
||||||
private boolean includeIndexNames = true; | ||||||
|
||||||
private String state; | ||||||
|
||||||
|
||||||
public GetSnapshotsRequest(TimeValue masterNodeTimeout) { | ||||||
super(masterNodeTimeout); | ||||||
} | ||||||
|
@@ -118,6 +122,9 @@ public GetSnapshotsRequest(StreamInput in) throws IOException { | |||||
if (in.getTransportVersion().onOrAfter(INDICES_FLAG_VERSION)) { | ||||||
includeIndexNames = in.readBoolean(); | ||||||
} | ||||||
if (in.getTransportVersion().onOrAfter(STATE_FLAG_VERSION)) { | ||||||
state = in.readOptionalString(); | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should avoid |
||||||
} | ||||||
|
||||||
@Override | ||||||
|
@@ -137,6 +144,9 @@ public void writeTo(StreamOutput out) throws IOException { | |||||
if (out.getTransportVersion().onOrAfter(INDICES_FLAG_VERSION)) { | ||||||
out.writeBoolean(includeIndexNames); | ||||||
} | ||||||
if (out.getTransportVersion().onOrAfter(STATE_FLAG_VERSION)) { | ||||||
out.writeOptionalString(state); | ||||||
} | ||||||
} | ||||||
|
||||||
@Override | ||||||
|
@@ -177,6 +187,12 @@ public ActionRequestValidationException validate() { | |||||
} else if (after != null && fromSortValue != null) { | ||||||
validationException = addValidationError("can't use after and from_sort_value simultaneously", validationException); | ||||||
} | ||||||
if (state != null && Arrays.stream(SnapshotState.values()).noneMatch(s -> s.name().equalsIgnoreCase(state))) { | ||||||
validationException = addValidationError( | ||||||
"state must be SUCCESS, IN_PROGRESS, FAILED, PARTIAL, or INCOMPATIBLE", | ||||||
validationException | ||||||
); | ||||||
} | ||||||
return validationException; | ||||||
} | ||||||
|
||||||
|
@@ -342,6 +358,15 @@ public boolean verbose() { | |||||
return verbose; | ||||||
} | ||||||
|
||||||
public String state() { | ||||||
return state; | ||||||
} | ||||||
|
||||||
public GetSnapshotsRequest state(String state) { | ||||||
this.state = state; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validation nit
Suggested change
|
||||||
return this; | ||||||
} | ||||||
|
||||||
@Override | ||||||
public Task createTask(long id, String type, String action, TaskId parentTaskId, Map<String, String> headers) { | ||||||
return new CancellableTask(id, type, action, getDescription(), parentTaskId, headers); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,7 +160,8 @@ protected void masterOperation( | |
request.size(), | ||
SnapshotsInProgress.get(state), | ||
request.verbose(), | ||
request.includeIndexNames() | ||
request.includeIndexNames(), | ||
request.state() | ||
).runOperation(listener); | ||
} | ||
|
||
|
@@ -181,6 +182,7 @@ private class GetSnapshotsOperation { | |
private final SnapshotNamePredicate snapshotNamePredicate; | ||
private final SnapshotPredicates fromSortValuePredicates; | ||
private final Predicate<String> slmPolicyPredicate; | ||
private final String state; | ||
|
||
// snapshot ordering/pagination | ||
private final SnapshotSortKey sortBy; | ||
|
@@ -224,7 +226,8 @@ private class GetSnapshotsOperation { | |
int size, | ||
SnapshotsInProgress snapshotsInProgress, | ||
boolean verbose, | ||
boolean indices | ||
boolean indices, | ||
String state | ||
) { | ||
this.cancellableTask = cancellableTask; | ||
this.repositories = repositories; | ||
|
@@ -237,6 +240,7 @@ private class GetSnapshotsOperation { | |
this.snapshotsInProgress = snapshotsInProgress; | ||
this.verbose = verbose; | ||
this.indices = indices; | ||
this.state = state; | ||
|
||
this.snapshotNamePredicate = SnapshotNamePredicate.forSnapshots(ignoreUnavailable, snapshots); | ||
this.fromSortValuePredicates = SnapshotPredicates.forFromSortValue(fromSortValue, sortBy, order); | ||
|
@@ -571,6 +575,10 @@ private boolean matchesPredicates(SnapshotInfo snapshotInfo) { | |
return false; | ||
} | ||
|
||
if (state != null) { | ||
return state.equalsIgnoreCase(snapshotInfo.state().toString()); | ||
|
||
} | ||
|
||
if (slmPolicyPredicate == SlmPolicyPredicate.MATCH_ALL_POLICIES) { | ||
return true; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we also add some simple smoke tests to
rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/snapshot.get/10_basic.yml
just to check that we are parsing the parameter ok. No need for anything exhaustive, just check that e.g.SUCCESS
andSUCCESS,PARTIAL
both return the expected snapshots.